c2767d325b663eaad396dfedb7ec145ac0177bf2
[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 #include <dirent.h>
17 #include <math.h>
18
19 #include "libgame/libgame.h"
20
21 #include "files.h"
22 #include "init.h"
23 #include "tools.h"
24 #include "tape.h"
25
26
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
28 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
29 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
30 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
32 #define LEVEL_HEADER_UNUSED     11      /* unused level header bytes  */
33 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
35 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
37 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
38 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
39 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
40 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
41 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
42
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
46
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN         0
54 #define LEVEL_FILE_TYPE_RND             1
55 #define LEVEL_FILE_TYPE_BD              2
56 #define LEVEL_FILE_TYPE_EM              3
57 #define LEVEL_FILE_TYPE_SP              4
58 #define LEVEL_FILE_TYPE_DX              5
59 #define LEVEL_FILE_TYPE_SB              6
60 #define LEVEL_FILE_TYPE_DC              7
61
62 #define LEVEL_FILE_TYPE_RND_PACKED      (10 + LEVEL_FILE_TYPE_RND)
63 #define LEVEL_FILE_TYPE_EM_PACKED       (10 + LEVEL_FILE_TYPE_EM)
64
65 #define IS_SINGLE_LEVEL_FILE(x)         (x < 10)
66 #define IS_PACKED_LEVEL_FILE(x)         (x > 10)
67
68
69 /* ========================================================================= */
70 /* level file functions                                                      */
71 /* ========================================================================= */
72
73 void setElementChangePages(struct ElementInfo *ei, int change_pages)
74 {
75   int change_page_size = sizeof(struct ElementChangeInfo);
76
77   ei->num_change_pages = MAX(1, change_pages);
78
79   ei->change_page =
80     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
81
82   if (ei->current_change_page >= ei->num_change_pages)
83     ei->current_change_page = ei->num_change_pages - 1;
84
85   ei->change = &ei->change_page[ei->current_change_page];
86 }
87
88 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
89 {
90   int x, y;
91
92   change->can_change = FALSE;
93
94   change->events = CE_BITMASK_DEFAULT;
95   change->sides = CH_SIDE_ANY;
96
97   change->target_element = EL_EMPTY_SPACE;
98
99   change->delay_fixed = 0;
100   change->delay_random = 0;
101   change->delay_frames = 1;
102
103   change->trigger_element = EL_EMPTY_SPACE;
104
105   change->explode = FALSE;
106   change->use_content = FALSE;
107   change->only_complete = FALSE;
108   change->use_random_change = FALSE;
109   change->random = 100;
110   change->power = CP_NON_DESTRUCTIVE;
111
112   for (x = 0; x < 3; x++)
113     for (y = 0; y < 3; y++)
114       change->content[x][y] = EL_EMPTY_SPACE;
115
116   change->direct_action = 0;
117   change->other_action = 0;
118
119   change->pre_change_function = NULL;
120   change->change_function = NULL;
121   change->post_change_function = NULL;
122 }
123
124 static void setLevelInfoToDefaults(struct LevelInfo *level)
125 {
126   int i, j, x, y;
127
128   level->file_version = FILE_VERSION_ACTUAL;
129   level->game_version = GAME_VERSION_ACTUAL;
130
131   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
132   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
133   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
134
135   level->fieldx = STD_LEV_FIELDX;
136   level->fieldy = STD_LEV_FIELDY;
137
138   for (x = 0; x < MAX_LEV_FIELDX; x++)
139     for (y = 0; y < MAX_LEV_FIELDY; y++)
140       level->field[x][y] = EL_SAND;
141
142   level->time = 100;
143   level->gems_needed = 0;
144   level->amoeba_speed = 10;
145   level->time_magic_wall = 10;
146   level->time_wheel = 10;
147   level->time_light = 10;
148   level->time_timegate = 10;
149   level->amoeba_content = EL_DIAMOND;
150   level->double_speed = FALSE;
151   level->initial_gravity = FALSE;
152   level->em_slippery_gems = FALSE;
153   level->block_last_field = FALSE;
154   level->sp_block_last_field = TRUE;
155
156   level->use_custom_template = FALSE;
157
158   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
159     level->name[i] = '\0';
160   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
161     level->author[i] = '\0';
162
163   strcpy(level->name, NAMELESS_LEVEL_NAME);
164   strcpy(level->author, ANONYMOUS_NAME);
165
166   for (i = 0; i < 4; i++)
167   {
168     level->envelope_text[i][0] = '\0';
169     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
170     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
171   }
172
173   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
174     level->score[i] = 10;
175
176   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
177   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
178     for (x = 0; x < 3; x++)
179       for (y = 0; y < 3; y++)
180         level->yamyam_content[i][x][y] =
181           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
182
183   level->field[0][0] = EL_PLAYER_1;
184   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
185
186   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
187   {
188     int element = i;
189
190     setElementChangePages(&element_info[element], 1);
191     setElementChangeInfoToDefaults(element_info[element].change);
192
193     if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
194     {
195       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
196         element_info[element].description[j] = '\0';
197
198       if (element_info[element].custom_description != NULL)
199         strncpy(element_info[element].description,
200                 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
201       else
202         strcpy(element_info[element].description,
203                element_info[element].editor_description);
204
205       element_info[element].use_gfx_element = FALSE;
206       element_info[element].gfx_element = EL_EMPTY_SPACE;
207     }
208
209     if (IS_CUSTOM_ELEMENT(element))
210     {
211       element_info[element].collect_score = 10;         /* special default */
212       element_info[element].collect_count = 1;          /* special default */
213
214       element_info[element].push_delay_fixed = -1;      /* initialize later */
215       element_info[element].push_delay_random = -1;     /* initialize later */
216       element_info[element].move_delay_fixed = 0;
217       element_info[element].move_delay_random = 0;
218
219       element_info[element].move_pattern = MV_ALL_DIRECTIONS;
220       element_info[element].move_direction_initial = MV_START_AUTOMATIC;
221       element_info[element].move_stepsize = TILEX / 8;
222       element_info[element].move_enter_element = EL_EMPTY_SPACE;
223       element_info[element].move_leave_element = EL_EMPTY_SPACE;
224       element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
225
226       element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
227
228       for (x = 0; x < 3; x++)
229         for (y = 0; y < 3; y++)
230           element_info[element].content[x][y] = EL_EMPTY_SPACE;
231
232       element_info[element].access_type = 0;
233       element_info[element].access_layer = 0;
234       element_info[element].access_protected = 0;
235       element_info[element].walk_to_action = 0;
236       element_info[element].smash_targets = 0;
237       element_info[element].deadliness = 0;
238       element_info[element].consistency = 0;
239
240       element_info[element].can_explode_by_fire = FALSE;
241       element_info[element].can_explode_smashed = FALSE;
242       element_info[element].can_explode_impact = FALSE;
243
244       element_info[element].current_change_page = 0;
245
246       /* start with no properties at all */
247       for (j = 0; j < NUM_EP_BITFIELDS; j++)
248         Properties[element][j] = EP_BITMASK_DEFAULT;
249
250       element_info[element].modified_settings = FALSE;
251     }
252     else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
253     {
254       /* initialize memory for list of elements in group */
255       if (element_info[element].group == NULL)
256         element_info[element].group =
257           checked_malloc(sizeof(struct ElementGroupInfo));
258
259       for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
260         element_info[element].group->element[j] = EL_EMPTY_SPACE;
261
262       /* default: only one element in group */
263       element_info[element].group->num_elements = 1;
264
265       element_info[element].group->choice_mode = ANIM_RANDOM;
266     }
267   }
268
269   BorderElement = EL_STEELWALL;
270
271   level->no_level_file = FALSE;
272
273   if (leveldir_current == NULL)         /* only when dumping level */
274     return;
275
276   /* try to determine better author name than 'anonymous' */
277   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
278   {
279     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
280     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
281   }
282   else
283   {
284     switch (LEVELCLASS(leveldir_current))
285     {
286       case LEVELCLASS_TUTORIAL:
287         strcpy(level->author, PROGRAM_AUTHOR_STRING);
288         break;
289
290       case LEVELCLASS_CONTRIB:
291         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
292         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
293         break;
294
295       case LEVELCLASS_PRIVATE:
296         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
297         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
298         break;
299
300       default:
301         /* keep default value */
302         break;
303     }
304   }
305 }
306
307 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
308 {
309   level_file_info->nr = 0;
310   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
311   level_file_info->packed = FALSE;
312   level_file_info->basename = NULL;
313   level_file_info->filename = NULL;
314 }
315
316 static void ActivateLevelTemplate()
317 {
318   /* Currently there is no special action needed to activate the template
319      data, because 'element_info' and 'Properties' overwrite the original
320      level data, while all other variables do not change. */
321 }
322
323 static char *getLevelFilenameFromBasename(char *basename)
324 {
325   static char *filename = NULL;
326
327   checked_free(filename);
328
329   filename = getPath2(getCurrentLevelDir(), basename);
330
331   return filename;
332 }
333
334 static int getFileTypeFromBasename(char *basename)
335 {
336   static char *filename = NULL;
337   struct stat file_status;
338
339   /* ---------- try to determine file type from filename ---------- */
340
341   /* check for typical filename of a Supaplex level package file */
342   if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
343                                  strncmp(basename, "LEVELS.D", 8) == 0))
344     return LEVEL_FILE_TYPE_SP;
345
346   /* ---------- try to determine file type from filesize ---------- */
347
348   checked_free(filename);
349   filename = getPath2(getCurrentLevelDir(), basename);
350
351   if (stat(filename, &file_status) == 0)
352   {
353     /* check for typical filesize of a Supaplex level package file */
354     if (file_status.st_size == 170496)
355       return LEVEL_FILE_TYPE_SP;
356   }
357
358   return LEVEL_FILE_TYPE_UNKNOWN;
359 }
360
361 static char *getSingleLevelBasename(int nr, int type)
362 {
363   static char basename[MAX_FILENAME_LEN];
364   char *level_filename = getStringCopy(leveldir_current->level_filename);
365
366   if (level_filename == NULL)
367     level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
368
369   switch (type)
370   {
371     case LEVEL_FILE_TYPE_RND:
372       if (nr < 0)
373         sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
374       else
375         sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
376       break;
377
378     case LEVEL_FILE_TYPE_EM:
379       sprintf(basename, "%d", nr);
380       break;
381
382     case LEVEL_FILE_TYPE_UNKNOWN:
383     default:
384       sprintf(basename, level_filename, nr);
385       break;
386   }
387
388   free(level_filename);
389
390   return basename;
391 }
392
393 static char *getPackedLevelBasename(int type)
394 {
395   static char basename[MAX_FILENAME_LEN];
396   char *directory = getCurrentLevelDir();
397   DIR *dir;
398   struct dirent *dir_entry;
399
400   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
401
402   if ((dir = opendir(directory)) == NULL)
403   {
404     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
405
406     return basename;
407   }
408
409   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
410   {
411     char *entry_basename = dir_entry->d_name;
412     int entry_type = getFileTypeFromBasename(entry_basename);
413
414     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
415     {
416       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
417           type == entry_type)
418       {
419         strcpy(basename, entry_basename);
420
421         break;
422       }
423     }
424   }
425
426   closedir(dir);
427
428   return basename;
429 }
430
431 static char *getSingleLevelFilename(int nr, int type)
432 {
433   return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
434 }
435
436 #if 0
437 static char *getPackedLevelFilename(int type)
438 {
439   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
440 }
441 #endif
442
443 char *getDefaultLevelFilename(int nr)
444 {
445   return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
446 }
447
448 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
449                                                  int type)
450 {
451   lfi->type = type;
452   lfi->packed = FALSE;
453   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
454   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
455 }
456
457 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
458                                                  int type)
459 {
460   lfi->type = type;
461   lfi->packed = TRUE;
462   lfi->basename = getPackedLevelBasename(lfi->type);
463   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
464 }
465
466 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
467 {
468   /* special case: level number is negative => check for level template file */
469   if (lfi->nr < 0)
470   {
471     setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
472
473     return;
474   }
475
476   if (leveldir_current->level_filename != NULL)
477   {
478     /* check for file name/pattern specified in "levelinfo.conf" */
479     setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
480     if (fileExists(lfi->filename))
481       return;
482   }
483
484   /* check for native Rocks'n'Diamonds level file */
485   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
486   if (fileExists(lfi->filename))
487     return;
488
489   /* check for classic Emerald Mine level file */
490   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
491   if (fileExists(lfi->filename))
492     return;
493
494   /* check for various packed level file formats */
495   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
496   if (fileExists(lfi->filename))
497     return;
498
499   /* no known level file found -- try to use default values */
500   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
501 }
502
503 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
504 {
505   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
506     lfi->type = getFileTypeFromBasename(lfi->basename);
507 }
508
509 static struct LevelFileInfo *getLevelFileInfo(int nr)
510 {
511   static struct LevelFileInfo level_file_info;
512
513   /* always start with reliable default values */
514   setFileInfoToDefaults(&level_file_info);
515
516   level_file_info.nr = nr;      /* set requested level number */
517
518   determineLevelFileInfo_Filename(&level_file_info);
519   determineLevelFileInfo_Filetype(&level_file_info);
520
521   return &level_file_info;
522 }
523
524
525 /* ------------------------------------------------------------------------- */
526 /* functions for loading R'n'D level                                         */
527 /* ------------------------------------------------------------------------- */
528
529 int getMappedElement(int element)
530 {
531   /* map some (historic, now obsolete) elements */
532
533 #if 1
534   switch (element)
535   {
536     case EL_PLAYER_OBSOLETE:
537       element = EL_PLAYER_1;
538       break;
539
540     case EL_KEY_OBSOLETE:
541       element = EL_KEY_1;
542
543     case EL_EM_KEY_1_FILE_OBSOLETE:
544       element = EL_EM_KEY_1;
545       break;
546
547     case EL_EM_KEY_2_FILE_OBSOLETE:
548       element = EL_EM_KEY_2;
549       break;
550
551     case EL_EM_KEY_3_FILE_OBSOLETE:
552       element = EL_EM_KEY_3;
553       break;
554
555     case EL_EM_KEY_4_FILE_OBSOLETE:
556       element = EL_EM_KEY_4;
557       break;
558
559     case EL_ENVELOPE_OBSOLETE:
560       element = EL_ENVELOPE_1;
561       break;
562
563     case EL_SP_EMPTY:
564       element = EL_EMPTY;
565       break;
566
567     default:
568       if (element >= NUM_FILE_ELEMENTS)
569       {
570         Error(ERR_WARN, "invalid level element %d", element);
571
572         element = EL_UNKNOWN;
573       }
574       break;
575   }
576 #else
577   if (element >= NUM_FILE_ELEMENTS)
578   {
579     Error(ERR_WARN, "invalid level element %d", element);
580
581     element = EL_UNKNOWN;
582   }
583   else if (element == EL_PLAYER_OBSOLETE)
584     element = EL_PLAYER_1;
585   else if (element == EL_KEY_OBSOLETE)
586     element = EL_KEY_1;
587 #endif
588
589   return element;
590 }
591
592 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
593 {
594   level->file_version = getFileVersion(file);
595   level->game_version = getFileVersion(file);
596
597   return chunk_size;
598 }
599
600 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
601 {
602   int i, x, y;
603
604   level->fieldx = getFile8Bit(file);
605   level->fieldy = getFile8Bit(file);
606
607   level->time           = getFile16BitBE(file);
608   level->gems_needed    = getFile16BitBE(file);
609
610   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
611     level->name[i] = getFile8Bit(file);
612   level->name[MAX_LEVEL_NAME_LEN] = 0;
613
614   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
615     level->score[i] = getFile8Bit(file);
616
617   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
618   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
619     for (y = 0; y < 3; y++)
620       for (x = 0; x < 3; x++)
621         level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
622
623   level->amoeba_speed           = getFile8Bit(file);
624   level->time_magic_wall        = getFile8Bit(file);
625   level->time_wheel             = getFile8Bit(file);
626   level->amoeba_content         = getMappedElement(getFile8Bit(file));
627   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
628   level->initial_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
629   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
630   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
631
632   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
633
634   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
635   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
636
637   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
638
639   return chunk_size;
640 }
641
642 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
643 {
644   int i;
645
646   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
647     level->author[i] = getFile8Bit(file);
648   level->author[MAX_LEVEL_NAME_LEN] = 0;
649
650   return chunk_size;
651 }
652
653 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
654 {
655   int x, y;
656   int chunk_size_expected = level->fieldx * level->fieldy;
657
658   /* Note: "chunk_size" was wrong before version 2.0 when elements are
659      stored with 16-bit encoding (and should be twice as big then).
660      Even worse, playfield data was stored 16-bit when only yamyam content
661      contained 16-bit elements and vice versa. */
662
663   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
664     chunk_size_expected *= 2;
665
666   if (chunk_size_expected != chunk_size)
667   {
668     ReadUnusedBytesFromFile(file, chunk_size);
669     return chunk_size_expected;
670   }
671
672   for (y = 0; y < level->fieldy; y++)
673     for (x = 0; x < level->fieldx; x++)
674       level->field[x][y] =
675         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
676                          getFile8Bit(file));
677   return chunk_size;
678 }
679
680 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
681 {
682   int i, x, y;
683   int header_size = 4;
684   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
685   int chunk_size_expected = header_size + content_size;
686
687   /* Note: "chunk_size" was wrong before version 2.0 when elements are
688      stored with 16-bit encoding (and should be twice as big then).
689      Even worse, playfield data was stored 16-bit when only yamyam content
690      contained 16-bit elements and vice versa. */
691
692   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
693     chunk_size_expected += content_size;
694
695   if (chunk_size_expected != chunk_size)
696   {
697     ReadUnusedBytesFromFile(file, chunk_size);
698     return chunk_size_expected;
699   }
700
701   getFile8Bit(file);
702   level->num_yamyam_contents = getFile8Bit(file);
703   getFile8Bit(file);
704   getFile8Bit(file);
705
706   /* correct invalid number of content fields -- should never happen */
707   if (level->num_yamyam_contents < 1 ||
708       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
709     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
710
711   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
712     for (y = 0; y < 3; y++)
713       for (x = 0; x < 3; x++)
714         level->yamyam_content[i][x][y] =
715           getMappedElement(level->encoding_16bit_field ?
716                            getFile16BitBE(file) : getFile8Bit(file));
717   return chunk_size;
718 }
719
720 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
721 {
722   int i, x, y;
723   int element;
724   int num_contents, content_xsize, content_ysize;
725   int content_array[MAX_ELEMENT_CONTENTS][3][3];
726
727   element = getMappedElement(getFile16BitBE(file));
728   num_contents = getFile8Bit(file);
729   content_xsize = getFile8Bit(file);
730   content_ysize = getFile8Bit(file);
731
732   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
733
734   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
735     for (y = 0; y < 3; y++)
736       for (x = 0; x < 3; x++)
737         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
738
739   /* correct invalid number of content fields -- should never happen */
740   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
741     num_contents = STD_ELEMENT_CONTENTS;
742
743   if (element == EL_YAMYAM)
744   {
745     level->num_yamyam_contents = num_contents;
746
747     for (i = 0; i < num_contents; i++)
748       for (y = 0; y < 3; y++)
749         for (x = 0; x < 3; x++)
750           level->yamyam_content[i][x][y] = content_array[i][x][y];
751   }
752   else if (element == EL_BD_AMOEBA)
753   {
754     level->amoeba_content = content_array[0][0][0];
755   }
756   else
757   {
758     Error(ERR_WARN, "cannot load content for element '%d'", element);
759   }
760
761   return chunk_size;
762 }
763
764 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
765 {
766   int i;
767   int element;
768   int envelope_nr;
769   int envelope_len;
770   int chunk_size_expected;
771
772   element = getMappedElement(getFile16BitBE(file));
773   if (!IS_ENVELOPE(element))
774     element = EL_ENVELOPE_1;
775
776   envelope_nr = element - EL_ENVELOPE_1;
777
778   envelope_len = getFile16BitBE(file);
779
780   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
781   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
782
783   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
784
785   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
786   if (chunk_size_expected != chunk_size)
787   {
788     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
789     return chunk_size_expected;
790   }
791
792   for (i = 0; i < envelope_len; i++)
793     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
794
795   return chunk_size;
796 }
797
798 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
799 {
800   int num_changed_custom_elements = getFile16BitBE(file);
801   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
802   int i;
803
804   if (chunk_size_expected != chunk_size)
805   {
806     ReadUnusedBytesFromFile(file, chunk_size - 2);
807     return chunk_size_expected;
808   }
809
810   for (i = 0; i < num_changed_custom_elements; i++)
811   {
812     int element = getFile16BitBE(file);
813     int properties = getFile32BitBE(file);
814
815     if (IS_CUSTOM_ELEMENT(element))
816       Properties[element][EP_BITFIELD_BASE] = properties;
817     else
818       Error(ERR_WARN, "invalid custom element number %d", element);
819   }
820
821   return chunk_size;
822 }
823
824 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
825 {
826   int num_changed_custom_elements = getFile16BitBE(file);
827   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
828   int i;
829
830   if (chunk_size_expected != chunk_size)
831   {
832     ReadUnusedBytesFromFile(file, chunk_size - 2);
833     return chunk_size_expected;
834   }
835
836   for (i = 0; i < num_changed_custom_elements; i++)
837   {
838     int element = getFile16BitBE(file);
839     int custom_target_element = getFile16BitBE(file);
840
841     if (IS_CUSTOM_ELEMENT(element))
842       element_info[element].change->target_element = custom_target_element;
843     else
844       Error(ERR_WARN, "invalid custom element number %d", element);
845   }
846
847   return chunk_size;
848 }
849
850 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
851 {
852   int num_changed_custom_elements = getFile16BitBE(file);
853   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
854   int i, j, x, y;
855
856   if (chunk_size_expected != chunk_size)
857   {
858     ReadUnusedBytesFromFile(file, chunk_size - 2);
859     return chunk_size_expected;
860   }
861
862   for (i = 0; i < num_changed_custom_elements; i++)
863   {
864     int element = getFile16BitBE(file);
865
866     if (!IS_CUSTOM_ELEMENT(element))
867     {
868       Error(ERR_WARN, "invalid custom element number %d", element);
869
870       element = EL_INTERNAL_DUMMY;
871     }
872
873     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
874       element_info[element].description[j] = getFile8Bit(file);
875     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
876
877     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
878
879     /* some free bytes for future properties and padding */
880     ReadUnusedBytesFromFile(file, 7);
881
882     element_info[element].use_gfx_element = getFile8Bit(file);
883     element_info[element].gfx_element =
884       getMappedElement(getFile16BitBE(file));
885
886     element_info[element].collect_score = getFile8Bit(file);
887     element_info[element].collect_count = getFile8Bit(file);
888
889     element_info[element].push_delay_fixed = getFile16BitBE(file);
890     element_info[element].push_delay_random = getFile16BitBE(file);
891     element_info[element].move_delay_fixed = getFile16BitBE(file);
892     element_info[element].move_delay_random = getFile16BitBE(file);
893
894     element_info[element].move_pattern = getFile16BitBE(file);
895     element_info[element].move_direction_initial = getFile8Bit(file);
896     element_info[element].move_stepsize = getFile8Bit(file);
897
898     for (y = 0; y < 3; y++)
899       for (x = 0; x < 3; x++)
900         element_info[element].content[x][y] =
901           getMappedElement(getFile16BitBE(file));
902
903     element_info[element].change->events = getFile32BitBE(file);
904
905     element_info[element].change->target_element =
906       getMappedElement(getFile16BitBE(file));
907
908     element_info[element].change->delay_fixed = getFile16BitBE(file);
909     element_info[element].change->delay_random = getFile16BitBE(file);
910     element_info[element].change->delay_frames = getFile16BitBE(file);
911
912     element_info[element].change->trigger_element =
913       getMappedElement(getFile16BitBE(file));
914
915     element_info[element].change->explode = getFile8Bit(file);
916     element_info[element].change->use_content = getFile8Bit(file);
917     element_info[element].change->only_complete = getFile8Bit(file);
918     element_info[element].change->use_random_change = getFile8Bit(file);
919
920     element_info[element].change->random = getFile8Bit(file);
921     element_info[element].change->power = getFile8Bit(file);
922
923     for (y = 0; y < 3; y++)
924       for (x = 0; x < 3; x++)
925         element_info[element].change->content[x][y] =
926           getMappedElement(getFile16BitBE(file));
927
928     element_info[element].slippery_type = getFile8Bit(file);
929
930     /* some free bytes for future properties and padding */
931     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
932
933     /* mark that this custom element has been modified */
934     element_info[element].modified_settings = TRUE;
935   }
936
937   return chunk_size;
938 }
939
940 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
941 {
942   struct ElementInfo *ei;
943   int chunk_size_expected;
944   int element;
945   int i, x, y;
946
947   element = getFile16BitBE(file);
948
949   if (!IS_CUSTOM_ELEMENT(element))
950   {
951     Error(ERR_WARN, "invalid custom element number %d", element);
952
953     ReadUnusedBytesFromFile(file, chunk_size - 2);
954     return chunk_size;
955   }
956
957   ei = &element_info[element];
958
959   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
960     ei->description[i] = getFile8Bit(file);
961   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
962
963   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
964   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
965
966   ei->num_change_pages = getFile8Bit(file);
967
968   /* some free bytes for future base property values and padding */
969   ReadUnusedBytesFromFile(file, 5);
970
971   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
972   if (chunk_size_expected != chunk_size)
973   {
974     ReadUnusedBytesFromFile(file, chunk_size - 48);
975     return chunk_size_expected;
976   }
977
978   /* read custom property values */
979
980   ei->use_gfx_element = getFile8Bit(file);
981   ei->gfx_element = getMappedElement(getFile16BitBE(file));
982
983   ei->collect_score = getFile8Bit(file);
984   ei->collect_count = getFile8Bit(file);
985
986   ei->push_delay_fixed = getFile16BitBE(file);
987   ei->push_delay_random = getFile16BitBE(file);
988   ei->move_delay_fixed = getFile16BitBE(file);
989   ei->move_delay_random = getFile16BitBE(file);
990
991   /* bits 0 - 15 of "move_pattern" ... */
992   ei->move_pattern = getFile16BitBE(file);
993   ei->move_direction_initial = getFile8Bit(file);
994   ei->move_stepsize = getFile8Bit(file);
995
996   ei->slippery_type = getFile8Bit(file);
997
998   for (y = 0; y < 3; y++)
999     for (x = 0; x < 3; x++)
1000       ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1001
1002   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1003   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1004   ei->move_leave_type = getFile8Bit(file);
1005
1006   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1007   ei->move_pattern |= (getFile16BitBE(file) << 16);
1008
1009   /* some free bytes for future custom property values and padding */
1010   ReadUnusedBytesFromFile(file, 5);
1011
1012   /* read change property values */
1013
1014   setElementChangePages(ei, ei->num_change_pages);
1015
1016   for (i = 0; i < ei->num_change_pages; i++)
1017   {
1018     struct ElementChangeInfo *change = &ei->change_page[i];
1019
1020     /* always start with reliable default values */
1021     setElementChangeInfoToDefaults(change);
1022
1023     change->events = getFile32BitBE(file);
1024
1025     change->target_element = getMappedElement(getFile16BitBE(file));
1026
1027     change->delay_fixed = getFile16BitBE(file);
1028     change->delay_random = getFile16BitBE(file);
1029     change->delay_frames = getFile16BitBE(file);
1030
1031     change->trigger_element = getMappedElement(getFile16BitBE(file));
1032
1033     change->explode = getFile8Bit(file);
1034     change->use_content = getFile8Bit(file);
1035     change->only_complete = getFile8Bit(file);
1036     change->use_random_change = getFile8Bit(file);
1037
1038     change->random = getFile8Bit(file);
1039     change->power = getFile8Bit(file);
1040
1041     for (y = 0; y < 3; y++)
1042       for (x = 0; x < 3; x++)
1043         change->content[x][y] = getMappedElement(getFile16BitBE(file));
1044
1045     change->can_change = getFile8Bit(file);
1046
1047     change->sides = getFile8Bit(file);
1048
1049     if (change->sides == CH_SIDE_NONE)  /* correct empty sides field */
1050       change->sides = CH_SIDE_ANY;
1051
1052     /* some free bytes for future change property values and padding */
1053     ReadUnusedBytesFromFile(file, 8);
1054   }
1055
1056   /* mark this custom element as modified */
1057   ei->modified_settings = TRUE;
1058
1059   return chunk_size;
1060 }
1061
1062 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1063 {
1064   struct ElementInfo *ei;
1065   struct ElementGroupInfo *group;
1066   int element;
1067   int i;
1068
1069   element = getFile16BitBE(file);
1070
1071   if (!IS_GROUP_ELEMENT(element))
1072   {
1073     Error(ERR_WARN, "invalid group element number %d", element);
1074
1075     ReadUnusedBytesFromFile(file, chunk_size - 2);
1076     return chunk_size;
1077   }
1078
1079   ei = &element_info[element];
1080
1081   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1082     ei->description[i] = getFile8Bit(file);
1083   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1084
1085   group = element_info[element].group;
1086
1087   group->num_elements = getFile8Bit(file);
1088
1089   ei->use_gfx_element = getFile8Bit(file);
1090   ei->gfx_element = getMappedElement(getFile16BitBE(file));
1091
1092   group->choice_mode = getFile8Bit(file);
1093
1094   /* some free bytes for future values and padding */
1095   ReadUnusedBytesFromFile(file, 3);
1096
1097   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1098     group->element[i] = getMappedElement(getFile16BitBE(file));
1099
1100   /* mark this group element as modified */
1101   element_info[element].modified_settings = TRUE;
1102
1103   return chunk_size;
1104 }
1105
1106 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1107                                       struct LevelFileInfo *level_file_info)
1108 {
1109   char *filename = level_file_info->filename;
1110   char cookie[MAX_LINE_LEN];
1111   char chunk_name[CHUNK_ID_LEN + 1];
1112   int chunk_size;
1113   FILE *file;
1114
1115   if (!(file = fopen(filename, MODE_READ)))
1116   {
1117     level->no_level_file = TRUE;
1118
1119     if (level != &level_template)
1120       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1121
1122     return;
1123   }
1124
1125   getFileChunkBE(file, chunk_name, NULL);
1126   if (strcmp(chunk_name, "RND1") == 0)
1127   {
1128     getFile32BitBE(file);               /* not used */
1129
1130     getFileChunkBE(file, chunk_name, NULL);
1131     if (strcmp(chunk_name, "CAVE") != 0)
1132     {
1133       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1134       fclose(file);
1135       return;
1136     }
1137   }
1138   else  /* check for pre-2.0 file format with cookie string */
1139   {
1140     strcpy(cookie, chunk_name);
1141     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1142     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1143       cookie[strlen(cookie) - 1] = '\0';
1144
1145     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1146     {
1147       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1148       fclose(file);
1149       return;
1150     }
1151
1152     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1153     {
1154       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1155       fclose(file);
1156       return;
1157     }
1158
1159     /* pre-2.0 level files have no game version, so use file version here */
1160     level->game_version = level->file_version;
1161   }
1162
1163   if (level->file_version < FILE_VERSION_1_2)
1164   {
1165     /* level files from versions before 1.2.0 without chunk structure */
1166     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
1167     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1168   }
1169   else
1170   {
1171     static struct
1172     {
1173       char *name;
1174       int size;
1175       int (*loader)(FILE *, int, struct LevelInfo *);
1176     }
1177     chunk_info[] =
1178     {
1179       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
1180       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
1181       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
1182       { "BODY", -1,                     LoadLevel_BODY },
1183       { "CONT", -1,                     LoadLevel_CONT },
1184       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
1185       { "CNT3", -1,                     LoadLevel_CNT3 },
1186       { "CUS1", -1,                     LoadLevel_CUS1 },
1187       { "CUS2", -1,                     LoadLevel_CUS2 },
1188       { "CUS3", -1,                     LoadLevel_CUS3 },
1189       { "CUS4", -1,                     LoadLevel_CUS4 },
1190       { "GRP1", -1,                     LoadLevel_GRP1 },
1191       {  NULL,  0,                      NULL }
1192     };
1193
1194     while (getFileChunkBE(file, chunk_name, &chunk_size))
1195     {
1196       int i = 0;
1197
1198       while (chunk_info[i].name != NULL &&
1199              strcmp(chunk_name, chunk_info[i].name) != 0)
1200         i++;
1201
1202       if (chunk_info[i].name == NULL)
1203       {
1204         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1205               chunk_name, filename);
1206         ReadUnusedBytesFromFile(file, chunk_size);
1207       }
1208       else if (chunk_info[i].size != -1 &&
1209                chunk_info[i].size != chunk_size)
1210       {
1211         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1212               chunk_size, chunk_name, filename);
1213         ReadUnusedBytesFromFile(file, chunk_size);
1214       }
1215       else
1216       {
1217         /* call function to load this level chunk */
1218         int chunk_size_expected =
1219           (chunk_info[i].loader)(file, chunk_size, level);
1220
1221         /* the size of some chunks cannot be checked before reading other
1222            chunks first (like "HEAD" and "BODY") that contain some header
1223            information, so check them here */
1224         if (chunk_size_expected != chunk_size)
1225         {
1226           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1227                 chunk_size, chunk_name, filename);
1228         }
1229       }
1230     }
1231   }
1232
1233   fclose(file);
1234 }
1235
1236 /* ------------------------------------------------------------------------- */
1237 /* functions for loading EM level                                            */
1238 /* ------------------------------------------------------------------------- */
1239
1240 static int map_em_element_yam(int element)
1241 {
1242   switch (element)
1243   {
1244     case 0x00:  return EL_EMPTY;
1245     case 0x01:  return EL_EMERALD;
1246     case 0x02:  return EL_DIAMOND;
1247     case 0x03:  return EL_ROCK;
1248     case 0x04:  return EL_ROBOT;
1249     case 0x05:  return EL_SPACESHIP_UP;
1250     case 0x06:  return EL_BOMB;
1251     case 0x07:  return EL_BUG_UP;
1252     case 0x08:  return EL_AMOEBA_DROP;
1253     case 0x09:  return EL_NUT;
1254     case 0x0a:  return EL_YAMYAM;
1255     case 0x0b:  return EL_QUICKSAND_FULL;
1256     case 0x0c:  return EL_SAND;
1257     case 0x0d:  return EL_WALL_SLIPPERY;
1258     case 0x0e:  return EL_STEELWALL;
1259     case 0x0f:  return EL_WALL;
1260     case 0x10:  return EL_EM_KEY_1;
1261     case 0x11:  return EL_EM_KEY_2;
1262     case 0x12:  return EL_EM_KEY_4;
1263     case 0x13:  return EL_EM_KEY_3;
1264     case 0x14:  return EL_MAGIC_WALL;
1265     case 0x15:  return EL_ROBOT_WHEEL;
1266     case 0x16:  return EL_DYNAMITE;
1267
1268     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
1269     case 0x18:  return EL_BUG_UP;                       /* EMC */
1270     case 0x1a:  return EL_DIAMOND;                      /* EMC */
1271     case 0x1b:  return EL_EMERALD;                      /* EMC */
1272     case 0x25:  return EL_NUT;                          /* EMC */
1273     case 0x80:  return EL_EMPTY;                        /* EMC */
1274     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
1275     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
1276     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
1277     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
1278     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
1279     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
1280     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
1281     case 0xbd:  return EL_SAND;                         /* EMC */
1282
1283     default:
1284       Error(ERR_WARN, "invalid level element %d", element);
1285       return EL_UNKNOWN;
1286   }
1287 }
1288
1289 static int map_em_element_field(int element)
1290 {
1291   if (element >= 0xc8 && element <= 0xe1)
1292     return EL_CHAR_A + (element - 0xc8);
1293   else if (element >= 0xe2 && element <= 0xeb)
1294     return EL_CHAR_0 + (element - 0xe2);
1295
1296   switch (element)
1297   {
1298     case 0x00:  return EL_ROCK;
1299     case 0x01:  return EL_ROCK;                         /* EMC */
1300     case 0x02:  return EL_DIAMOND;
1301     case 0x03:  return EL_DIAMOND;
1302     case 0x04:  return EL_ROBOT;
1303     case 0x05:  return EL_ROBOT;                        /* EMC */
1304     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
1305     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
1306     case 0x08:  return EL_SPACESHIP_UP;
1307     case 0x09:  return EL_SPACESHIP_RIGHT;
1308     case 0x0a:  return EL_SPACESHIP_DOWN;
1309     case 0x0b:  return EL_SPACESHIP_LEFT;
1310     case 0x0c:  return EL_SPACESHIP_UP;
1311     case 0x0d:  return EL_SPACESHIP_RIGHT;
1312     case 0x0e:  return EL_SPACESHIP_DOWN;
1313     case 0x0f:  return EL_SPACESHIP_LEFT;
1314
1315     case 0x10:  return EL_BOMB;
1316     case 0x11:  return EL_BOMB;                         /* EMC */
1317     case 0x12:  return EL_EMERALD;
1318     case 0x13:  return EL_EMERALD;
1319     case 0x14:  return EL_BUG_UP;
1320     case 0x15:  return EL_BUG_RIGHT;
1321     case 0x16:  return EL_BUG_DOWN;
1322     case 0x17:  return EL_BUG_LEFT;
1323     case 0x18:  return EL_BUG_UP;
1324     case 0x19:  return EL_BUG_RIGHT;
1325     case 0x1a:  return EL_BUG_DOWN;
1326     case 0x1b:  return EL_BUG_LEFT;
1327     case 0x1c:  return EL_AMOEBA_DROP;
1328     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
1329     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
1330     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
1331
1332     case 0x20:  return EL_ROCK;
1333     case 0x21:  return EL_BOMB;                         /* EMC */
1334     case 0x22:  return EL_DIAMOND;                      /* EMC */
1335     case 0x23:  return EL_EMERALD;                      /* EMC */
1336     case 0x24:  return EL_MAGIC_WALL;
1337     case 0x25:  return EL_NUT;
1338     case 0x26:  return EL_NUT;                          /* EMC */
1339     case 0x27:  return EL_NUT;                          /* EMC */
1340
1341       /* looks like magic wheel, but is _always_ activated */
1342     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
1343
1344     case 0x29:  return EL_YAMYAM;       /* up */
1345     case 0x2a:  return EL_YAMYAM;       /* down */
1346     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
1347     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
1348     case 0x2d:  return EL_QUICKSAND_FULL;
1349     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
1350     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
1351
1352     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
1353     case 0x31:  return EL_SAND;                         /* EMC */
1354     case 0x32:  return EL_SAND;                         /* EMC */
1355     case 0x33:  return EL_SAND;                         /* EMC */
1356     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
1357     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
1358     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
1359     case 0x37:  return EL_SAND;                         /* EMC */
1360     case 0x38:  return EL_ROCK;                         /* EMC */
1361     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
1362     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
1363     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
1364     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
1365     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
1366     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
1367     case 0x3f:  return EL_ACID_POOL_BOTTOM;
1368
1369     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
1370     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
1371     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
1372     case 0x43:  return EL_BALLOON;                      /* EMC */
1373     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
1374     case 0x45:  return EL_SPRING;                       /* EMC */
1375     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
1376     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
1377     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
1378     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
1379     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
1380     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
1381     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
1382     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
1383     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
1384     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
1385
1386     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
1387     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
1388     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
1389     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
1390     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
1391     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
1392     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
1393     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
1394     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
1395     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
1396     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
1397     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
1398     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
1399     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
1400     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
1401     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
1402
1403     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
1404     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
1405     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
1406     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
1407     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
1408     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
1409     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
1410     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
1411     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
1412     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
1413     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
1414     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
1415     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
1416     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
1417     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
1418     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
1419
1420     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
1421     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
1422     case 0x72:  return EL_NUT;          /* left */      /* EMC */
1423     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
1424     case 0x74:  return EL_STEELWALL;
1425     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
1426     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
1427     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
1428     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
1429     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
1430     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
1431     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
1432     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
1433     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
1434     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
1435     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
1436
1437     case 0x80:  return EL_EMPTY;
1438     case 0x81:  return EL_WALL_SLIPPERY;
1439     case 0x82:  return EL_SAND;
1440     case 0x83:  return EL_STEELWALL;
1441     case 0x84:  return EL_WALL;
1442     case 0x85:  return EL_EM_KEY_1;
1443     case 0x86:  return EL_EM_KEY_2;
1444     case 0x87:  return EL_EM_KEY_4;
1445     case 0x88:  return EL_EM_KEY_3;
1446     case 0x89:  return EL_EM_GATE_1;
1447     case 0x8a:  return EL_EM_GATE_2;
1448     case 0x8b:  return EL_EM_GATE_4;
1449     case 0x8c:  return EL_EM_GATE_3;
1450     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
1451     case 0x8e:  return EL_EM_GATE_1_GRAY;
1452     case 0x8f:  return EL_EM_GATE_2_GRAY;
1453
1454     case 0x90:  return EL_EM_GATE_4_GRAY;
1455     case 0x91:  return EL_EM_GATE_3_GRAY;
1456     case 0x92:  return EL_MAGIC_WALL;
1457     case 0x93:  return EL_ROBOT_WHEEL;
1458     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
1459     case 0x95:  return EL_ACID_POOL_TOPLEFT;
1460     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
1461     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
1462     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
1463     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
1464     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
1465     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
1466     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
1467     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
1468     case 0x9e:  return EL_EXIT_CLOSED;
1469     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
1470
1471       /* looks like normal sand, but behaves like wall */
1472     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1473     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
1474     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
1475     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
1476     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1477     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
1478     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
1479     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
1480     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
1481     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
1482     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
1483     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
1484     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
1485     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
1486     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
1487     case 0xaf:  return EL_DYNAMITE;
1488
1489     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
1490     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
1491     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
1492     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
1493     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
1494     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
1495     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
1496     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
1497     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
1498     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
1499     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
1500     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
1501     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
1502     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
1503     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
1504     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
1505
1506     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
1507     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
1508     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
1509     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
1510     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
1511     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
1512     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
1513     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
1514
1515       /* characters: see above */
1516
1517     case 0xec:  return EL_CHAR_PERIOD;
1518     case 0xed:  return EL_CHAR_EXCLAM;
1519     case 0xee:  return EL_CHAR_COLON;
1520     case 0xef:  return EL_CHAR_QUESTION;
1521
1522     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
1523     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
1524     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
1525     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
1526     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
1527     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
1528     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
1529     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
1530
1531     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
1532     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
1533     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
1534     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
1535     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
1536     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
1537
1538     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
1539     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
1540
1541     default:
1542       /* should never happen (all 8-bit value cases should be handled) */
1543       Error(ERR_WARN, "invalid level element %d", element);
1544       return EL_UNKNOWN;
1545   }
1546 }
1547
1548 #define EM_LEVEL_SIZE                   2106
1549 #define EM_LEVEL_XSIZE                  64
1550 #define EM_LEVEL_YSIZE                  32
1551
1552 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1553                                      struct LevelFileInfo *level_file_info)
1554 {
1555   char *filename = level_file_info->filename;
1556   FILE *file;
1557   unsigned char leveldata[EM_LEVEL_SIZE];
1558   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1559   unsigned char code0 = 0x65;
1560   unsigned char code1 = 0x11;
1561   boolean level_is_crypted = FALSE;
1562   int nr = level_file_info->nr;
1563   int i, x, y;
1564
1565   if (!(file = fopen(filename, MODE_READ)))
1566   {
1567     level->no_level_file = TRUE;
1568
1569     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1570
1571     return;
1572   }
1573
1574   for(i = 0; i < EM_LEVEL_SIZE; i++)
1575     leveldata[i] = fgetc(file);
1576
1577   fclose(file);
1578
1579   /* check if level data is crypted by testing against known starting bytes
1580      of the few existing crypted level files (from Emerald Mine 1 + 2) */
1581
1582   if ((leveldata[0] == 0xf1 ||
1583        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1584   {
1585     level_is_crypted = TRUE;
1586
1587     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
1588       leveldata[0] = 0xf1;
1589   }
1590
1591   if (level_is_crypted)         /* decode crypted level data */
1592   {
1593     for(i = 0; i < EM_LEVEL_SIZE; i++)
1594     {
1595       leveldata[i] ^= code0;
1596       leveldata[i] -= code1;
1597
1598       code0  = (code0 + 7) & 0xff;
1599     }
1600   }
1601
1602   level->fieldx = EM_LEVEL_XSIZE;
1603   level->fieldy = EM_LEVEL_YSIZE;
1604
1605   level->time           = header[46] * 10;
1606   level->gems_needed    = header[47];
1607
1608   /* The original Emerald Mine levels have their level number stored
1609      at the second byte of the level file...
1610      Do not trust this information at other level files, e.g. EMC,
1611      but correct it anyway (normally the first row is completely
1612      steel wall, so the correction does not hurt anyway). */
1613
1614   if (leveldata[1] == nr)
1615     leveldata[1] = leveldata[2];        /* correct level number field */
1616
1617   sprintf(level->name, "Level %d", nr);         /* set level name */
1618
1619   level->score[SC_EMERALD]      = header[36];
1620   level->score[SC_DIAMOND]      = header[37];
1621   level->score[SC_ROBOT]        = header[38];
1622   level->score[SC_SPACESHIP]    = header[39];
1623   level->score[SC_BUG]          = header[40];
1624   level->score[SC_YAMYAM]       = header[41];
1625   level->score[SC_NUT]          = header[42];
1626   level->score[SC_DYNAMITE]     = header[43];
1627   level->score[SC_TIME_BONUS]   = header[44];
1628
1629   level->num_yamyam_contents = 4;
1630
1631   for(i = 0; i < level->num_yamyam_contents; i++)
1632     for(y = 0; y < 3; y++)
1633       for(x = 0; x < 3; x++)
1634         level->yamyam_content[i][x][y] =
1635           map_em_element_yam(header[i * 9 + y * 3 + x]);
1636
1637   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
1638   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
1639   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
1640   level->amoeba_content         = EL_DIAMOND;
1641
1642   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1643   {
1644     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1645
1646     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1647       new_element = EL_AMOEBA_WET;
1648
1649     level->field[x][y] = new_element;
1650   }
1651
1652   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1653   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1654   level->field[x][y] = EL_PLAYER_1;
1655
1656   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1657   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1658   level->field[x][y] = EL_PLAYER_2;
1659 }
1660
1661 /* ------------------------------------------------------------------------- */
1662 /* functions for loading SP level                                            */
1663 /* ------------------------------------------------------------------------- */
1664
1665 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1666 #define SP_LEVEL_SIZE                   1536
1667 #define SP_LEVEL_XSIZE                  60
1668 #define SP_LEVEL_YSIZE                  24
1669 #define SP_LEVEL_NAME_LEN               23
1670
1671 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1672                                        int nr)
1673 {
1674   int i, x, y;
1675
1676   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1677   for (y = 0; y < SP_LEVEL_YSIZE; y++)
1678   {
1679     for (x = 0; x < SP_LEVEL_XSIZE; x++)
1680     {
1681       int element_old = fgetc(file);
1682       int element_new;
1683
1684       if (element_old <= 0x27)
1685         element_new = getMappedElement(EL_SP_START + element_old);
1686       else if (element_old == 0x28)
1687         element_new = EL_INVISIBLE_WALL;
1688       else
1689       {
1690         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1691         Error(ERR_WARN, "invalid level element %d", element_old);
1692
1693         element_new = EL_UNKNOWN;
1694       }
1695
1696       level->field[x][y] = element_new;
1697     }
1698   }
1699
1700   ReadUnusedBytesFromFile(file, 4);
1701
1702   /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1703   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1704
1705   ReadUnusedBytesFromFile(file, 1);
1706
1707   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1708   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1709     level->name[i] = fgetc(file);
1710   level->name[SP_LEVEL_NAME_LEN] = '\0';
1711
1712   /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1713   ReadUnusedBytesFromFile(file, 1);     /* !!! NOT SUPPORTED YET !!! */
1714
1715   /* number of infotrons needed; 0 means that Supaplex will count the total
1716      amount of infotrons in the level and use the low byte of that number.
1717      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1718   level->gems_needed = fgetc(file);
1719
1720   /* information about special gravity port entries */
1721   ReadUnusedBytesFromFile(file, 65);    /* !!! NOT SUPPORTED YET !!! */
1722
1723   level->fieldx = SP_LEVEL_XSIZE;
1724   level->fieldy = SP_LEVEL_YSIZE;
1725
1726   level->time = 0;                      /* no time limit */
1727   level->amoeba_speed = 0;
1728   level->time_magic_wall = 0;
1729   level->time_wheel = 0;
1730   level->amoeba_content = EL_EMPTY;
1731
1732   for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1733     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
1734
1735   /* there are no yamyams in supaplex levels */
1736   for(i = 0; i < level->num_yamyam_contents; i++)
1737     for(y = 0; y < 3; y++)
1738       for(x = 0; x < 3; x++)
1739         level->yamyam_content[i][x][y] = EL_EMPTY;
1740 }
1741
1742 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1743                                      struct LevelFileInfo *level_file_info)
1744 {
1745   char *filename = level_file_info->filename;
1746   FILE *file;
1747   int nr = level_file_info->nr - leveldir_current->first_level;
1748   int i, l, x, y;
1749   char name_first, name_last;
1750   struct LevelInfo multipart_level;
1751   int multipart_xpos, multipart_ypos;
1752   boolean is_multipart_level;
1753   boolean is_first_part;
1754   boolean reading_multipart_level = FALSE;
1755   boolean use_empty_level = FALSE;
1756
1757   if (!(file = fopen(filename, MODE_READ)))
1758   {
1759     level->no_level_file = TRUE;
1760
1761     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1762
1763     return;
1764   }
1765
1766   /* position file stream to the requested level inside the level package */
1767   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1768   {
1769     level->no_level_file = TRUE;
1770
1771     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1772
1773     return;
1774   }
1775
1776   /* there exist Supaplex level package files with multi-part levels which
1777      can be detected as follows: instead of leading and trailing dashes ('-')
1778      to pad the level name, they have leading and trailing numbers which are
1779      the x and y coordinations of the current part of the multi-part level;
1780      if there are '?' characters instead of numbers on the left or right side
1781      of the level name, the multi-part level consists of only horizontal or
1782      vertical parts */
1783
1784   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1785   {
1786     LoadLevelFromFileStream_SP(file, level, l);
1787
1788     /* check if this level is a part of a bigger multi-part level */
1789
1790     name_first = level->name[0];
1791     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
1792
1793     is_multipart_level =
1794       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1795        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
1796
1797     is_first_part =
1798       ((name_first == '?' || name_first == '1') &&
1799        (name_last  == '?' || name_last  == '1'));
1800
1801     /* correct leading multipart level meta information in level name */
1802     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1803       level->name[i] = '-';
1804
1805     /* correct trailing multipart level meta information in level name */
1806     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1807       level->name[i] = '-';
1808
1809     /* ---------- check for normal single level ---------- */
1810
1811     if (!reading_multipart_level && !is_multipart_level)
1812     {
1813       /* the current level is simply a normal single-part level, and we are
1814          not reading a multi-part level yet, so return the level as it is */
1815
1816       break;
1817     }
1818
1819     /* ---------- check for empty level (unused multi-part) ---------- */
1820
1821     if (!reading_multipart_level && is_multipart_level && !is_first_part)
1822     {
1823       /* this is a part of a multi-part level, but not the first part
1824          (and we are not already reading parts of a multi-part level);
1825          in this case, use an empty level instead of the single part */
1826
1827       use_empty_level = TRUE;
1828
1829       break;
1830     }
1831
1832     /* ---------- check for finished multi-part level ---------- */
1833
1834     if (reading_multipart_level &&
1835         (!is_multipart_level ||
1836          strcmp(level->name, multipart_level.name) != 0))
1837     {
1838       /* we are already reading parts of a multi-part level, but this level is
1839          either not a multi-part level, or a part of a different multi-part
1840          level; in both cases, the multi-part level seems to be complete */
1841
1842       break;
1843     }
1844
1845     /* ---------- here we have one part of a multi-part level ---------- */
1846
1847     reading_multipart_level = TRUE;
1848
1849     if (is_first_part)  /* start with first part of new multi-part level */
1850     {
1851       /* copy level info structure from first part */
1852       multipart_level = *level;
1853
1854       /* clear playfield of new multi-part level */
1855       for (y = 0; y < MAX_LEV_FIELDY; y++)
1856         for (x = 0; x < MAX_LEV_FIELDX; x++)
1857           multipart_level.field[x][y] = EL_EMPTY;
1858     }
1859
1860     if (name_first == '?')
1861       name_first = '1';
1862     if (name_last == '?')
1863       name_last = '1';
1864
1865     multipart_xpos = (int)(name_first - '0');
1866     multipart_ypos = (int)(name_last  - '0');
1867
1868 #if 0
1869     printf("----------> part (%d/%d) of multi-part level '%s'\n",
1870            multipart_xpos, multipart_ypos, multipart_level.name);
1871 #endif
1872
1873     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1874         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1875     {
1876       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1877
1878       break;
1879     }
1880
1881     multipart_level.fieldx = MAX(multipart_level.fieldx,
1882                                  multipart_xpos * SP_LEVEL_XSIZE);
1883     multipart_level.fieldy = MAX(multipart_level.fieldy,
1884                                  multipart_ypos * SP_LEVEL_YSIZE);
1885
1886     /* copy level part at the right position of multi-part level */
1887     for (y = 0; y < SP_LEVEL_YSIZE; y++)
1888     {
1889       for (x = 0; x < SP_LEVEL_XSIZE; x++)
1890       {
1891         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1892         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1893
1894         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1895       }
1896     }
1897   }
1898
1899   fclose(file);
1900
1901   if (use_empty_level)
1902   {
1903     setLevelInfoToDefaults(level);
1904
1905     level->fieldx = SP_LEVEL_XSIZE;
1906     level->fieldy = SP_LEVEL_YSIZE;
1907
1908     for (y = 0; y < SP_LEVEL_YSIZE; y++)
1909       for (x = 0; x < SP_LEVEL_XSIZE; x++)
1910         level->field[x][y] = EL_EMPTY;
1911
1912     strcpy(level->name, "-------- EMPTY --------");
1913
1914     Error(ERR_WARN, "single part of multi-part level -- using empty level");
1915   }
1916
1917   if (reading_multipart_level)
1918     *level = multipart_level;
1919 }
1920
1921 /* ------------------------------------------------------------------------- */
1922 /* functions for loading generic level                                       */
1923 /* ------------------------------------------------------------------------- */
1924
1925 void LoadLevelFromFileInfo(struct LevelInfo *level,
1926                            struct LevelFileInfo *level_file_info)
1927 {
1928   /* always start with reliable default values */
1929   setLevelInfoToDefaults(level);
1930
1931   switch (level_file_info->type)
1932   {
1933     case LEVEL_FILE_TYPE_RND:
1934       LoadLevelFromFileInfo_RND(level, level_file_info);
1935       break;
1936
1937     case LEVEL_FILE_TYPE_EM:
1938       LoadLevelFromFileInfo_EM(level, level_file_info);
1939       break;
1940
1941     case LEVEL_FILE_TYPE_SP:
1942       LoadLevelFromFileInfo_SP(level, level_file_info);
1943       break;
1944
1945     default:
1946       LoadLevelFromFileInfo_RND(level, level_file_info);
1947       break;
1948   }
1949 }
1950
1951 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1952 {
1953   static struct LevelFileInfo level_file_info;
1954
1955   /* always start with reliable default values */
1956   setFileInfoToDefaults(&level_file_info);
1957
1958   level_file_info.nr = 0;                       /* unknown level number */
1959   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
1960   level_file_info.filename = filename;
1961
1962   LoadLevelFromFileInfo(level, &level_file_info);
1963 }
1964
1965 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1966 {
1967   if (leveldir_current == NULL)         /* only when dumping level */
1968     return;
1969
1970 #if 0
1971   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1972 #endif
1973
1974   /* determine correct game engine version of current level */
1975 #if 1
1976   if (!leveldir_current->latest_engine)
1977 #else
1978   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1979       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1980       IS_LEVELCLASS_UNDEFINED(leveldir_current))
1981 #endif
1982   {
1983 #if 0
1984     printf("\n::: This level is private or contributed: '%s'\n", filename);
1985 #endif
1986
1987 #if 0
1988     printf("\n::: Use the stored game engine version for this level\n");
1989 #endif
1990
1991     /* For all levels which are not forced to use the latest game engine
1992        version (normally user contributed, private and undefined levels),
1993        use the version of the game engine the levels were created for.
1994
1995        Since 2.0.1, the game engine version is now directly stored
1996        in the level file (chunk "VERS"), so there is no need anymore
1997        to set the game version from the file version (except for old,
1998        pre-2.0 levels, where the game version is still taken from the
1999        file format version used to store the level -- see above). */
2000
2001     /* do some special adjustments to support older level versions */
2002     if (level->file_version == FILE_VERSION_1_0)
2003     {
2004       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2005       Error(ERR_WARN, "using high speed movement for player");
2006
2007       /* player was faster than monsters in (pre-)1.0 levels */
2008       level->double_speed = TRUE;
2009     }
2010
2011     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2012     if (level->game_version == VERSION_IDENT(2,0,1,0))
2013       level->em_slippery_gems = TRUE;
2014   }
2015   else
2016   {
2017 #if 0
2018     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2019            leveldir_current->sort_priority, filename);
2020 #endif
2021
2022 #if 0
2023     printf("\n::: Use latest game engine version for this level.\n");
2024 #endif
2025
2026     /* For all levels which are forced to use the latest game engine version
2027        (normally all but user contributed, private and undefined levels), set
2028        the game engine version to the actual version; this allows for actual
2029        corrections in the game engine to take effect for existing, converted
2030        levels (from "classic" or other existing games) to make the emulation
2031        of the corresponding game more accurate, while (hopefully) not breaking
2032        existing levels created from other players. */
2033
2034 #if 0
2035     printf("::: changing engine from %d to %d\n",
2036            level->game_version, GAME_VERSION_ACTUAL);
2037 #endif
2038
2039     level->game_version = GAME_VERSION_ACTUAL;
2040
2041     /* Set special EM style gems behaviour: EM style gems slip down from
2042        normal, steel and growing wall. As this is a more fundamental change,
2043        it seems better to set the default behaviour to "off" (as it is more
2044        natural) and make it configurable in the level editor (as a property
2045        of gem style elements). Already existing converted levels (neither
2046        private nor contributed levels) are changed to the new behaviour. */
2047
2048     if (level->file_version < FILE_VERSION_2_0)
2049       level->em_slippery_gems = TRUE;
2050   }
2051
2052 #if 0
2053   printf("::: => %d\n", level->game_version);
2054 #endif
2055 }
2056
2057 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2058 {
2059   int i, j;
2060
2061   /* map custom element change events that have changed in newer versions
2062      (these following values were accidentally changed in version 3.0.1) */
2063   if (level->game_version <= VERSION_IDENT(3,0,0,0))
2064   {
2065     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2066     {
2067       int element = EL_CUSTOM_START + i;
2068
2069       /* order of checking and copying events to be mapped is important */
2070       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2071       {
2072         if (HAS_CHANGE_EVENT(element, j - 2))
2073         {
2074           SET_CHANGE_EVENT(element, j - 2, FALSE);
2075           SET_CHANGE_EVENT(element, j, TRUE);
2076         }
2077       }
2078
2079       /* order of checking and copying events to be mapped is important */
2080       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2081       {
2082         if (HAS_CHANGE_EVENT(element, j - 1))
2083         {
2084           SET_CHANGE_EVENT(element, j - 1, FALSE);
2085           SET_CHANGE_EVENT(element, j, TRUE);
2086         }
2087       }
2088     }
2089   }
2090
2091   /* some custom element change events get mapped since version 3.0.3 */
2092   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2093   {
2094     int element = EL_CUSTOM_START + i;
2095
2096     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2097         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2098     {
2099       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2100       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2101
2102       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2103     }
2104   }
2105
2106   /* initialize "can_change" field for old levels with only one change page */
2107   if (level->game_version <= VERSION_IDENT(3,0,2,0))
2108   {
2109     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2110     {
2111       int element = EL_CUSTOM_START + i;
2112
2113       if (CAN_CHANGE(element))
2114         element_info[element].change->can_change = TRUE;
2115     }
2116   }
2117
2118 #if 0
2119   /* set default push delay values (corrected since version 3.0.7-1) */
2120   if (level->game_version < VERSION_IDENT(3,0,7,1))
2121   {
2122     game.default_push_delay_fixed = 2;
2123     game.default_push_delay_random = 8;
2124   }
2125   else
2126   {
2127     game.default_push_delay_fixed = 8;
2128     game.default_push_delay_random = 8;
2129   }
2130
2131   /* set uninitialized push delay values of custom elements in older levels */
2132   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2133   {
2134     int element = EL_CUSTOM_START + i;
2135
2136     if (element_info[element].push_delay_fixed == -1)
2137       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2138     if (element_info[element].push_delay_random == -1)
2139       element_info[element].push_delay_random = game.default_push_delay_random;
2140   }
2141 #endif
2142
2143   /* initialize element properties for level editor etc. */
2144   InitElementPropertiesEngine(level->game_version);
2145 }
2146
2147 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2148 {
2149   int x, y;
2150
2151   /* map elements that have changed in newer versions */
2152   for (y = 0; y < level->fieldy; y++)
2153   {
2154     for (x = 0; x < level->fieldx; x++)
2155     {
2156       int element = level->field[x][y];
2157
2158       if (level->game_version <= VERSION_IDENT(2,2,0,0))
2159       {
2160         /* map game font elements */
2161         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2162                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2163                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2164                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2165       }
2166
2167       if (level->game_version < VERSION_IDENT(3,0,0,0))
2168       {
2169         /* map Supaplex gravity tube elements */
2170         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2171                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2172                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2173                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2174                    element);
2175       }
2176
2177       level->field[x][y] = element;
2178     }
2179   }
2180
2181   /* copy elements to runtime playfield array */
2182   for (x = 0; x < MAX_LEV_FIELDX; x++)
2183     for (y = 0; y < MAX_LEV_FIELDY; y++)
2184       Feld[x][y] = level->field[x][y];
2185
2186   /* initialize level size variables for faster access */
2187   lev_fieldx = level->fieldx;
2188   lev_fieldy = level->fieldy;
2189
2190   /* determine border element for this level */
2191   SetBorderElement();
2192 }
2193
2194 void LoadLevelTemplate(int nr)
2195 {
2196 #if 1
2197   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2198   char *filename = level_file_info->filename;
2199
2200   LoadLevelFromFileInfo(&level_template, level_file_info);
2201 #else
2202   char *filename = getDefaultLevelFilename(nr);
2203
2204   LoadLevelFromFilename_RND(&level_template, filename);
2205 #endif
2206
2207   LoadLevel_InitVersion(&level, filename);
2208   LoadLevel_InitElements(&level, filename);
2209
2210   ActivateLevelTemplate();
2211 }
2212
2213 void LoadLevel(int nr)
2214 {
2215 #if 1
2216   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2217   char *filename = level_file_info->filename;
2218
2219   LoadLevelFromFileInfo(&level, level_file_info);
2220 #else
2221   char *filename = getLevelFilename(nr);
2222
2223   LoadLevelFromFilename_RND(&level, filename);
2224 #endif
2225
2226   if (level.use_custom_template)
2227     LoadLevelTemplate(-1);
2228
2229 #if 1
2230   LoadLevel_InitVersion(&level, filename);
2231   LoadLevel_InitElements(&level, filename);
2232   LoadLevel_InitPlayfield(&level, filename);
2233 #else
2234   LoadLevel_InitLevel(&level, filename);
2235 #endif
2236 }
2237
2238 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2239 {
2240   putFileVersion(file, level->file_version);
2241   putFileVersion(file, level->game_version);
2242 }
2243
2244 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2245 {
2246   int i, x, y;
2247
2248   putFile8Bit(file, level->fieldx);
2249   putFile8Bit(file, level->fieldy);
2250
2251   putFile16BitBE(file, level->time);
2252   putFile16BitBE(file, level->gems_needed);
2253
2254   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2255     putFile8Bit(file, level->name[i]);
2256
2257   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2258     putFile8Bit(file, level->score[i]);
2259
2260   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2261     for (y = 0; y < 3; y++)
2262       for (x = 0; x < 3; x++)
2263         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2264                            level->yamyam_content[i][x][y]));
2265   putFile8Bit(file, level->amoeba_speed);
2266   putFile8Bit(file, level->time_magic_wall);
2267   putFile8Bit(file, level->time_wheel);
2268   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2269                      level->amoeba_content));
2270   putFile8Bit(file, (level->double_speed ? 1 : 0));
2271   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2272   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2273   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2274
2275   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2276
2277   putFile8Bit(file, (level->block_last_field ? 1 : 0));
2278   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2279
2280   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2281 }
2282
2283 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2284 {
2285   int i;
2286
2287   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2288     putFile8Bit(file, level->author[i]);
2289 }
2290
2291 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2292 {
2293   int x, y;
2294
2295   for (y = 0; y < level->fieldy; y++) 
2296     for (x = 0; x < level->fieldx; x++) 
2297       if (level->encoding_16bit_field)
2298         putFile16BitBE(file, level->field[x][y]);
2299       else
2300         putFile8Bit(file, level->field[x][y]);
2301 }
2302
2303 #if 0
2304 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2305 {
2306   int i, x, y;
2307
2308   putFile8Bit(file, EL_YAMYAM);
2309   putFile8Bit(file, level->num_yamyam_contents);
2310   putFile8Bit(file, 0);
2311   putFile8Bit(file, 0);
2312
2313   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2314     for (y = 0; y < 3; y++)
2315       for (x = 0; x < 3; x++)
2316         if (level->encoding_16bit_field)
2317           putFile16BitBE(file, level->yamyam_content[i][x][y]);
2318         else
2319           putFile8Bit(file, level->yamyam_content[i][x][y]);
2320 }
2321 #endif
2322
2323 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2324 {
2325   int i, x, y;
2326   int num_contents, content_xsize, content_ysize;
2327   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2328
2329   if (element == EL_YAMYAM)
2330   {
2331     num_contents = level->num_yamyam_contents;
2332     content_xsize = 3;
2333     content_ysize = 3;
2334
2335     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2336       for (y = 0; y < 3; y++)
2337         for (x = 0; x < 3; x++)
2338           content_array[i][x][y] = level->yamyam_content[i][x][y];
2339   }
2340   else if (element == EL_BD_AMOEBA)
2341   {
2342     num_contents = 1;
2343     content_xsize = 1;
2344     content_ysize = 1;
2345
2346     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2347       for (y = 0; y < 3; y++)
2348         for (x = 0; x < 3; x++)
2349           content_array[i][x][y] = EL_EMPTY;
2350     content_array[0][0][0] = level->amoeba_content;
2351   }
2352   else
2353   {
2354     /* chunk header already written -- write empty chunk data */
2355     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2356
2357     Error(ERR_WARN, "cannot save content for element '%d'", element);
2358     return;
2359   }
2360
2361   putFile16BitBE(file, element);
2362   putFile8Bit(file, num_contents);
2363   putFile8Bit(file, content_xsize);
2364   putFile8Bit(file, content_ysize);
2365
2366   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2367
2368   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2369     for (y = 0; y < 3; y++)
2370       for (x = 0; x < 3; x++)
2371         putFile16BitBE(file, content_array[i][x][y]);
2372 }
2373
2374 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2375 {
2376   int i;
2377   int envelope_nr = element - EL_ENVELOPE_1;
2378   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2379
2380   putFile16BitBE(file, element);
2381   putFile16BitBE(file, envelope_len);
2382   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2383   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2384
2385   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2386
2387   for (i = 0; i < envelope_len; i++)
2388     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2389 }
2390
2391 #if 0
2392 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2393                            int num_changed_custom_elements)
2394 {
2395   int i, check = 0;
2396
2397   putFile16BitBE(file, num_changed_custom_elements);
2398
2399   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2400   {
2401     int element = EL_CUSTOM_START + i;
2402
2403     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2404     {
2405       if (check < num_changed_custom_elements)
2406       {
2407         putFile16BitBE(file, element);
2408         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2409       }
2410
2411       check++;
2412     }
2413   }
2414
2415   if (check != num_changed_custom_elements)     /* should not happen */
2416     Error(ERR_WARN, "inconsistent number of custom element properties");
2417 }
2418 #endif
2419
2420 #if 0
2421 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2422                            int num_changed_custom_elements)
2423 {
2424   int i, check = 0;
2425
2426   putFile16BitBE(file, num_changed_custom_elements);
2427
2428   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2429   {
2430     int element = EL_CUSTOM_START + i;
2431
2432     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2433     {
2434       if (check < num_changed_custom_elements)
2435       {
2436         putFile16BitBE(file, element);
2437         putFile16BitBE(file, element_info[element].change->target_element);
2438       }
2439
2440       check++;
2441     }
2442   }
2443
2444   if (check != num_changed_custom_elements)     /* should not happen */
2445     Error(ERR_WARN, "inconsistent number of custom target elements");
2446 }
2447 #endif
2448
2449 #if 0
2450 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2451                            int num_changed_custom_elements)
2452 {
2453   int i, j, x, y, check = 0;
2454
2455   putFile16BitBE(file, num_changed_custom_elements);
2456
2457   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2458   {
2459     int element = EL_CUSTOM_START + i;
2460
2461     if (element_info[element].modified_settings)
2462     {
2463       if (check < num_changed_custom_elements)
2464       {
2465         putFile16BitBE(file, element);
2466
2467         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2468           putFile8Bit(file, element_info[element].description[j]);
2469
2470         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2471
2472         /* some free bytes for future properties and padding */
2473         WriteUnusedBytesToFile(file, 7);
2474
2475         putFile8Bit(file, element_info[element].use_gfx_element);
2476         putFile16BitBE(file, element_info[element].gfx_element);
2477
2478         putFile8Bit(file, element_info[element].collect_score);
2479         putFile8Bit(file, element_info[element].collect_count);
2480
2481         putFile16BitBE(file, element_info[element].push_delay_fixed);
2482         putFile16BitBE(file, element_info[element].push_delay_random);
2483         putFile16BitBE(file, element_info[element].move_delay_fixed);
2484         putFile16BitBE(file, element_info[element].move_delay_random);
2485
2486         putFile16BitBE(file, element_info[element].move_pattern);
2487         putFile8Bit(file, element_info[element].move_direction_initial);
2488         putFile8Bit(file, element_info[element].move_stepsize);
2489
2490         for (y = 0; y < 3; y++)
2491           for (x = 0; x < 3; x++)
2492             putFile16BitBE(file, element_info[element].content[x][y]);
2493
2494         putFile32BitBE(file, element_info[element].change->events);
2495
2496         putFile16BitBE(file, element_info[element].change->target_element);
2497
2498         putFile16BitBE(file, element_info[element].change->delay_fixed);
2499         putFile16BitBE(file, element_info[element].change->delay_random);
2500         putFile16BitBE(file, element_info[element].change->delay_frames);
2501
2502         putFile16BitBE(file, element_info[element].change->trigger_element);
2503
2504         putFile8Bit(file, element_info[element].change->explode);
2505         putFile8Bit(file, element_info[element].change->use_content);
2506         putFile8Bit(file, element_info[element].change->only_complete);
2507         putFile8Bit(file, element_info[element].change->use_random_change);
2508
2509         putFile8Bit(file, element_info[element].change->random);
2510         putFile8Bit(file, element_info[element].change->power);
2511
2512         for (y = 0; y < 3; y++)
2513           for (x = 0; x < 3; x++)
2514             putFile16BitBE(file, element_info[element].change->content[x][y]);
2515
2516         putFile8Bit(file, element_info[element].slippery_type);
2517
2518         /* some free bytes for future properties and padding */
2519         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2520       }
2521
2522       check++;
2523     }
2524   }
2525
2526   if (check != num_changed_custom_elements)     /* should not happen */
2527     Error(ERR_WARN, "inconsistent number of custom element properties");
2528 }
2529 #endif
2530
2531 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2532 {
2533   struct ElementInfo *ei = &element_info[element];
2534   int i, x, y;
2535
2536   putFile16BitBE(file, element);
2537
2538   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2539     putFile8Bit(file, ei->description[i]);
2540
2541   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2542   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
2543
2544   putFile8Bit(file, ei->num_change_pages);
2545
2546   /* some free bytes for future base property values and padding */
2547   WriteUnusedBytesToFile(file, 5);
2548
2549   /* write custom property values */
2550
2551   putFile8Bit(file, ei->use_gfx_element);
2552   putFile16BitBE(file, ei->gfx_element);
2553
2554   putFile8Bit(file, ei->collect_score);
2555   putFile8Bit(file, ei->collect_count);
2556
2557   putFile16BitBE(file, ei->push_delay_fixed);
2558   putFile16BitBE(file, ei->push_delay_random);
2559   putFile16BitBE(file, ei->move_delay_fixed);
2560   putFile16BitBE(file, ei->move_delay_random);
2561
2562   /* bits 0 - 15 of "move_pattern" ... */
2563   putFile16BitBE(file, ei->move_pattern & 0xffff);
2564   putFile8Bit(file, ei->move_direction_initial);
2565   putFile8Bit(file, ei->move_stepsize);
2566
2567   putFile8Bit(file, ei->slippery_type);
2568
2569   for (y = 0; y < 3; y++)
2570     for (x = 0; x < 3; x++)
2571       putFile16BitBE(file, ei->content[x][y]);
2572
2573   putFile16BitBE(file, ei->move_enter_element);
2574   putFile16BitBE(file, ei->move_leave_element);
2575   putFile8Bit(file, ei->move_leave_type);
2576
2577   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2578   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2579
2580   /* some free bytes for future custom property values and padding */
2581   WriteUnusedBytesToFile(file, 5);
2582
2583   /* write change property values */
2584
2585   for (i = 0; i < ei->num_change_pages; i++)
2586   {
2587     struct ElementChangeInfo *change = &ei->change_page[i];
2588
2589     putFile32BitBE(file, change->events);
2590
2591     putFile16BitBE(file, change->target_element);
2592
2593     putFile16BitBE(file, change->delay_fixed);
2594     putFile16BitBE(file, change->delay_random);
2595     putFile16BitBE(file, change->delay_frames);
2596
2597     putFile16BitBE(file, change->trigger_element);
2598
2599     putFile8Bit(file, change->explode);
2600     putFile8Bit(file, change->use_content);
2601     putFile8Bit(file, change->only_complete);
2602     putFile8Bit(file, change->use_random_change);
2603
2604     putFile8Bit(file, change->random);
2605     putFile8Bit(file, change->power);
2606
2607     for (y = 0; y < 3; y++)
2608       for (x = 0; x < 3; x++)
2609         putFile16BitBE(file, change->content[x][y]);
2610
2611     putFile8Bit(file, change->can_change);
2612
2613     putFile8Bit(file, change->sides);
2614
2615     /* some free bytes for future change property values and padding */
2616     WriteUnusedBytesToFile(file, 8);
2617   }
2618 }
2619
2620 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2621 {
2622   struct ElementInfo *ei = &element_info[element];
2623   struct ElementGroupInfo *group = ei->group;
2624   int i;
2625
2626   putFile16BitBE(file, element);
2627
2628   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2629     putFile8Bit(file, ei->description[i]);
2630
2631   putFile8Bit(file, group->num_elements);
2632
2633   putFile8Bit(file, ei->use_gfx_element);
2634   putFile16BitBE(file, ei->gfx_element);
2635
2636   putFile8Bit(file, group->choice_mode);
2637
2638   /* some free bytes for future values and padding */
2639   WriteUnusedBytesToFile(file, 3);
2640
2641   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2642     putFile16BitBE(file, group->element[i]);
2643 }
2644
2645 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2646 {
2647   int body_chunk_size;
2648   int i, x, y;
2649   FILE *file;
2650
2651   if (!(file = fopen(filename, MODE_WRITE)))
2652   {
2653     Error(ERR_WARN, "cannot save level file '%s'", filename);
2654     return;
2655   }
2656
2657   level->file_version = FILE_VERSION_ACTUAL;
2658   level->game_version = GAME_VERSION_ACTUAL;
2659
2660   /* check level field for 16-bit elements */
2661   level->encoding_16bit_field = FALSE;
2662   for (y = 0; y < level->fieldy; y++) 
2663     for (x = 0; x < level->fieldx; x++) 
2664       if (level->field[x][y] > 255)
2665         level->encoding_16bit_field = TRUE;
2666
2667   /* check yamyam content for 16-bit elements */
2668   level->encoding_16bit_yamyam = FALSE;
2669   for (i = 0; i < level->num_yamyam_contents; i++)
2670     for (y = 0; y < 3; y++)
2671       for (x = 0; x < 3; x++)
2672         if (level->yamyam_content[i][x][y] > 255)
2673           level->encoding_16bit_yamyam = TRUE;
2674
2675   /* check amoeba content for 16-bit elements */
2676   level->encoding_16bit_amoeba = FALSE;
2677   if (level->amoeba_content > 255)
2678     level->encoding_16bit_amoeba = TRUE;
2679
2680   /* calculate size of "BODY" chunk */
2681   body_chunk_size =
2682     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2683
2684   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2685   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2686
2687   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2688   SaveLevel_VERS(file, level);
2689
2690   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2691   SaveLevel_HEAD(file, level);
2692
2693   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2694   SaveLevel_AUTH(file, level);
2695
2696   putFileChunkBE(file, "BODY", body_chunk_size);
2697   SaveLevel_BODY(file, level);
2698
2699   if (level->encoding_16bit_yamyam ||
2700       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2701   {
2702     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2703     SaveLevel_CNT2(file, level, EL_YAMYAM);
2704   }
2705
2706   if (level->encoding_16bit_amoeba)
2707   {
2708     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2709     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2710   }
2711
2712   /* check for envelope content */
2713   for (i = 0; i < 4; i++)
2714   {
2715     if (strlen(level->envelope_text[i]) > 0)
2716     {
2717       int envelope_len = strlen(level->envelope_text[i]) + 1;
2718
2719       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2720       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2721     }
2722   }
2723
2724   /* check for non-default custom elements (unless using template level) */
2725   if (!level->use_custom_template)
2726   {
2727     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2728     {
2729       int element = EL_CUSTOM_START + i;
2730
2731       if (element_info[element].modified_settings)
2732       {
2733         int num_change_pages = element_info[element].num_change_pages;
2734
2735         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2736         SaveLevel_CUS4(file, level, element);
2737       }
2738     }
2739   }
2740
2741   /* check for non-default group elements (unless using template level) */
2742   if (!level->use_custom_template)
2743   {
2744     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2745     {
2746       int element = EL_GROUP_START + i;
2747
2748       if (element_info[element].modified_settings)
2749       {
2750         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2751         SaveLevel_GRP1(file, level, element);
2752       }
2753     }
2754   }
2755
2756   fclose(file);
2757
2758   SetFilePermissions(filename, PERMS_PRIVATE);
2759 }
2760
2761 void SaveLevel(int nr)
2762 {
2763   char *filename = getDefaultLevelFilename(nr);
2764
2765   SaveLevelFromFilename(&level, filename);
2766 }
2767
2768 void SaveLevelTemplate()
2769 {
2770   char *filename = getDefaultLevelFilename(-1);
2771
2772   SaveLevelFromFilename(&level, filename);
2773 }
2774
2775 void DumpLevel(struct LevelInfo *level)
2776 {
2777   printf_line("-", 79);
2778   printf("Level xxx (file version %08d, game version %08d)\n",
2779          level->file_version, level->game_version);
2780   printf_line("-", 79);
2781
2782   printf("Level author: '%s'\n", level->author);
2783   printf("Level title:  '%s'\n", level->name);
2784   printf("\n");
2785   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2786   printf("\n");
2787   printf("Level time:  %d seconds\n", level->time);
2788   printf("Gems needed: %d\n", level->gems_needed);
2789   printf("\n");
2790   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2791   printf("Time for wheel:      %d seconds\n", level->time_wheel);
2792   printf("Time for light:      %d seconds\n", level->time_light);
2793   printf("Time for timegate:   %d seconds\n", level->time_timegate);
2794   printf("\n");
2795   printf("Amoeba speed: %d\n", level->amoeba_speed);
2796   printf("\n");
2797   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
2798   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
2799   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
2800   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
2801   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2802
2803   printf_line("-", 79);
2804 }
2805
2806
2807 /* ========================================================================= */
2808 /* tape file functions                                                       */
2809 /* ========================================================================= */
2810
2811 static void setTapeInfoToDefaults()
2812 {
2813   int i;
2814
2815   /* always start with reliable default values (empty tape) */
2816   TapeErase();
2817
2818   /* default values (also for pre-1.2 tapes) with only the first player */
2819   tape.player_participates[0] = TRUE;
2820   for (i = 1; i < MAX_PLAYERS; i++)
2821     tape.player_participates[i] = FALSE;
2822
2823   /* at least one (default: the first) player participates in every tape */
2824   tape.num_participating_players = 1;
2825
2826   tape.level_nr = level_nr;
2827   tape.counter = 0;
2828   tape.changed = FALSE;
2829
2830   tape.recording = FALSE;
2831   tape.playing = FALSE;
2832   tape.pausing = FALSE;
2833 }
2834
2835 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2836 {
2837   tape->file_version = getFileVersion(file);
2838   tape->game_version = getFileVersion(file);
2839
2840   return chunk_size;
2841 }
2842
2843 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2844 {
2845   int i;
2846
2847   tape->random_seed = getFile32BitBE(file);
2848   tape->date        = getFile32BitBE(file);
2849   tape->length      = getFile32BitBE(file);
2850
2851   /* read header fields that are new since version 1.2 */
2852   if (tape->file_version >= FILE_VERSION_1_2)
2853   {
2854     byte store_participating_players = getFile8Bit(file);
2855     int engine_version;
2856
2857     /* since version 1.2, tapes store which players participate in the tape */
2858     tape->num_participating_players = 0;
2859     for (i = 0; i < MAX_PLAYERS; i++)
2860     {
2861       tape->player_participates[i] = FALSE;
2862
2863       if (store_participating_players & (1 << i))
2864       {
2865         tape->player_participates[i] = TRUE;
2866         tape->num_participating_players++;
2867       }
2868     }
2869
2870     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2871
2872     engine_version = getFileVersion(file);
2873     if (engine_version > 0)
2874       tape->engine_version = engine_version;
2875     else
2876       tape->engine_version = tape->game_version;
2877   }
2878
2879   return chunk_size;
2880 }
2881
2882 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2883 {
2884   int level_identifier_size;
2885   int i;
2886
2887   level_identifier_size = getFile16BitBE(file);
2888
2889   tape->level_identifier =
2890     checked_realloc(tape->level_identifier, level_identifier_size);
2891
2892   for (i = 0; i < level_identifier_size; i++)
2893     tape->level_identifier[i] = getFile8Bit(file);
2894
2895   tape->level_nr = getFile16BitBE(file);
2896
2897   chunk_size = 2 + level_identifier_size + 2;
2898
2899   return chunk_size;
2900 }
2901
2902 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2903 {
2904   int i, j;
2905   int chunk_size_expected =
2906     (tape->num_participating_players + 1) * tape->length;
2907
2908   if (chunk_size_expected != chunk_size)
2909   {
2910     ReadUnusedBytesFromFile(file, chunk_size);
2911     return chunk_size_expected;
2912   }
2913
2914   for (i = 0; i < tape->length; i++)
2915   {
2916     if (i >= MAX_TAPELEN)
2917       break;
2918
2919     for (j = 0; j < MAX_PLAYERS; j++)
2920     {
2921       tape->pos[i].action[j] = MV_NO_MOVING;
2922
2923       if (tape->player_participates[j])
2924         tape->pos[i].action[j] = getFile8Bit(file);
2925     }
2926
2927     tape->pos[i].delay = getFile8Bit(file);
2928
2929     if (tape->file_version == FILE_VERSION_1_0)
2930     {
2931       /* eliminate possible diagonal moves in old tapes */
2932       /* this is only for backward compatibility */
2933
2934       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2935       byte action = tape->pos[i].action[0];
2936       int k, num_moves = 0;
2937
2938       for (k = 0; k<4; k++)
2939       {
2940         if (action & joy_dir[k])
2941         {
2942           tape->pos[i + num_moves].action[0] = joy_dir[k];
2943           if (num_moves > 0)
2944             tape->pos[i + num_moves].delay = 0;
2945           num_moves++;
2946         }
2947       }
2948
2949       if (num_moves > 1)
2950       {
2951         num_moves--;
2952         i += num_moves;
2953         tape->length += num_moves;
2954       }
2955     }
2956     else if (tape->file_version < FILE_VERSION_2_0)
2957     {
2958       /* convert pre-2.0 tapes to new tape format */
2959
2960       if (tape->pos[i].delay > 1)
2961       {
2962         /* action part */
2963         tape->pos[i + 1] = tape->pos[i];
2964         tape->pos[i + 1].delay = 1;
2965
2966         /* delay part */
2967         for (j = 0; j < MAX_PLAYERS; j++)
2968           tape->pos[i].action[j] = MV_NO_MOVING;
2969         tape->pos[i].delay--;
2970
2971         i++;
2972         tape->length++;
2973       }
2974     }
2975
2976     if (feof(file))
2977       break;
2978   }
2979
2980   if (i != tape->length)
2981     chunk_size = (tape->num_participating_players + 1) * i;
2982
2983   return chunk_size;
2984 }
2985
2986 void LoadTapeFromFilename(char *filename)
2987 {
2988   char cookie[MAX_LINE_LEN];
2989   char chunk_name[CHUNK_ID_LEN + 1];
2990   FILE *file;
2991   int chunk_size;
2992
2993   /* always start with reliable default values */
2994   setTapeInfoToDefaults();
2995
2996   if (!(file = fopen(filename, MODE_READ)))
2997     return;
2998
2999   getFileChunkBE(file, chunk_name, NULL);
3000   if (strcmp(chunk_name, "RND1") == 0)
3001   {
3002     getFile32BitBE(file);               /* not used */
3003
3004     getFileChunkBE(file, chunk_name, NULL);
3005     if (strcmp(chunk_name, "TAPE") != 0)
3006     {
3007       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3008       fclose(file);
3009       return;
3010     }
3011   }
3012   else  /* check for pre-2.0 file format with cookie string */
3013   {
3014     strcpy(cookie, chunk_name);
3015     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3016     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3017       cookie[strlen(cookie) - 1] = '\0';
3018
3019     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3020     {
3021       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3022       fclose(file);
3023       return;
3024     }
3025
3026     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3027     {
3028       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3029       fclose(file);
3030       return;
3031     }
3032
3033     /* pre-2.0 tape files have no game version, so use file version here */
3034     tape.game_version = tape.file_version;
3035   }
3036
3037   if (tape.file_version < FILE_VERSION_1_2)
3038   {
3039     /* tape files from versions before 1.2.0 without chunk structure */
3040     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3041     LoadTape_BODY(file, 2 * tape.length,  &tape);
3042   }
3043   else
3044   {
3045     static struct
3046     {
3047       char *name;
3048       int size;
3049       int (*loader)(FILE *, int, struct TapeInfo *);
3050     }
3051     chunk_info[] =
3052     {
3053       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
3054       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
3055       { "INFO", -1,                     LoadTape_INFO },
3056       { "BODY", -1,                     LoadTape_BODY },
3057       {  NULL,  0,                      NULL }
3058     };
3059
3060     while (getFileChunkBE(file, chunk_name, &chunk_size))
3061     {
3062       int i = 0;
3063
3064       while (chunk_info[i].name != NULL &&
3065              strcmp(chunk_name, chunk_info[i].name) != 0)
3066         i++;
3067
3068       if (chunk_info[i].name == NULL)
3069       {
3070         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3071               chunk_name, filename);
3072         ReadUnusedBytesFromFile(file, chunk_size);
3073       }
3074       else if (chunk_info[i].size != -1 &&
3075                chunk_info[i].size != chunk_size)
3076       {
3077         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3078               chunk_size, chunk_name, filename);
3079         ReadUnusedBytesFromFile(file, chunk_size);
3080       }
3081       else
3082       {
3083         /* call function to load this tape chunk */
3084         int chunk_size_expected =
3085           (chunk_info[i].loader)(file, chunk_size, &tape);
3086
3087         /* the size of some chunks cannot be checked before reading other
3088            chunks first (like "HEAD" and "BODY") that contain some header
3089            information, so check them here */
3090         if (chunk_size_expected != chunk_size)
3091         {
3092           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3093                 chunk_size, chunk_name, filename);
3094         }
3095       }
3096     }
3097   }
3098
3099   fclose(file);
3100
3101   tape.length_seconds = GetTapeLength();
3102
3103 #if 0
3104   printf("::: tape game version: %d\n", tape.game_version);
3105   printf("::: tape engine version: %d\n", tape.engine_version);
3106 #endif
3107 }
3108
3109 void LoadTape(int nr)
3110 {
3111   char *filename = getTapeFilename(nr);
3112
3113   LoadTapeFromFilename(filename);
3114 }
3115
3116 void LoadSolutionTape(int nr)
3117 {
3118   char *filename = getSolutionTapeFilename(nr);
3119
3120   LoadTapeFromFilename(filename);
3121 }
3122
3123 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3124 {
3125   putFileVersion(file, tape->file_version);
3126   putFileVersion(file, tape->game_version);
3127 }
3128
3129 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3130 {
3131   int i;
3132   byte store_participating_players = 0;
3133
3134   /* set bits for participating players for compact storage */
3135   for (i = 0; i < MAX_PLAYERS; i++)
3136     if (tape->player_participates[i])
3137       store_participating_players |= (1 << i);
3138
3139   putFile32BitBE(file, tape->random_seed);
3140   putFile32BitBE(file, tape->date);
3141   putFile32BitBE(file, tape->length);
3142
3143   putFile8Bit(file, store_participating_players);
3144
3145   /* unused bytes not at the end here for 4-byte alignment of engine_version */
3146   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3147
3148   putFileVersion(file, tape->engine_version);
3149 }
3150
3151 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3152 {
3153   int level_identifier_size = strlen(tape->level_identifier) + 1;
3154   int i;
3155
3156   putFile16BitBE(file, level_identifier_size);
3157
3158   for (i = 0; i < level_identifier_size; i++)
3159     putFile8Bit(file, tape->level_identifier[i]);
3160
3161   putFile16BitBE(file, tape->level_nr);
3162 }
3163
3164 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3165 {
3166   int i, j;
3167
3168   for (i = 0; i < tape->length; i++)
3169   {
3170     for (j = 0; j < MAX_PLAYERS; j++)
3171       if (tape->player_participates[j])
3172         putFile8Bit(file, tape->pos[i].action[j]);
3173
3174     putFile8Bit(file, tape->pos[i].delay);
3175   }
3176 }
3177
3178 void SaveTape(int nr)
3179 {
3180   char *filename = getTapeFilename(nr);
3181   FILE *file;
3182   boolean new_tape = TRUE;
3183   int num_participating_players = 0;
3184   int info_chunk_size;
3185   int body_chunk_size;
3186   int i;
3187
3188   InitTapeDirectory(leveldir_current->subdir);
3189
3190   /* if a tape still exists, ask to overwrite it */
3191   if (access(filename, F_OK) == 0)
3192   {
3193     new_tape = FALSE;
3194     if (!Request("Replace old tape ?", REQ_ASK))
3195       return;
3196   }
3197
3198   if (!(file = fopen(filename, MODE_WRITE)))
3199   {
3200     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3201     return;
3202   }
3203
3204   tape.file_version = FILE_VERSION_ACTUAL;
3205   tape.game_version = GAME_VERSION_ACTUAL;
3206
3207   /* count number of participating players  */
3208   for (i = 0; i < MAX_PLAYERS; i++)
3209     if (tape.player_participates[i])
3210       num_participating_players++;
3211
3212   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3213   body_chunk_size = (num_participating_players + 1) * tape.length;
3214
3215   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3216   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3217
3218   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3219   SaveTape_VERS(file, &tape);
3220
3221   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3222   SaveTape_HEAD(file, &tape);
3223
3224   putFileChunkBE(file, "INFO", info_chunk_size);
3225   SaveTape_INFO(file, &tape);
3226
3227   putFileChunkBE(file, "BODY", body_chunk_size);
3228   SaveTape_BODY(file, &tape);
3229
3230   fclose(file);
3231
3232   SetFilePermissions(filename, PERMS_PRIVATE);
3233
3234   tape.changed = FALSE;
3235
3236   if (new_tape)
3237     Request("tape saved !", REQ_CONFIRM);
3238 }
3239
3240 void DumpTape(struct TapeInfo *tape)
3241 {
3242   int i, j;
3243
3244   if (TAPE_IS_EMPTY(*tape))
3245   {
3246     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3247     return;
3248   }
3249
3250   printf_line("-", 79);
3251   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3252          tape->level_nr, tape->file_version, tape->game_version);
3253   printf("Level series identifier: '%s'\n", tape->level_identifier);
3254   printf_line("-", 79);
3255
3256   for (i = 0; i < tape->length; i++)
3257   {
3258     if (i >= MAX_TAPELEN)
3259       break;
3260
3261     printf("%03d: ", i);
3262
3263     for (j = 0; j < MAX_PLAYERS; j++)
3264     {
3265       if (tape->player_participates[j])
3266       {
3267         int action = tape->pos[i].action[j];
3268
3269         printf("%d:%02x ", j, action);
3270         printf("[%c%c%c%c|%c%c] - ",
3271                (action & JOY_LEFT ? '<' : ' '),
3272                (action & JOY_RIGHT ? '>' : ' '),
3273                (action & JOY_UP ? '^' : ' '),
3274                (action & JOY_DOWN ? 'v' : ' '),
3275                (action & JOY_BUTTON_1 ? '1' : ' '),
3276                (action & JOY_BUTTON_2 ? '2' : ' '));
3277       }
3278     }
3279
3280     printf("(%03d)\n", tape->pos[i].delay);
3281   }
3282
3283   printf_line("-", 79);
3284 }
3285
3286
3287 /* ========================================================================= */
3288 /* score file functions                                                      */
3289 /* ========================================================================= */
3290
3291 void LoadScore(int nr)
3292 {
3293   int i;
3294   char *filename = getScoreFilename(nr);
3295   char cookie[MAX_LINE_LEN];
3296   char line[MAX_LINE_LEN];
3297   char *line_ptr;
3298   FILE *file;
3299
3300   /* always start with reliable default values */
3301   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3302   {
3303     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3304     highscore[i].Score = 0;
3305   }
3306
3307   if (!(file = fopen(filename, MODE_READ)))
3308     return;
3309
3310   /* check file identifier */
3311   fgets(cookie, MAX_LINE_LEN, file);
3312   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3313     cookie[strlen(cookie) - 1] = '\0';
3314
3315   if (!checkCookieString(cookie, SCORE_COOKIE))
3316   {
3317     Error(ERR_WARN, "unknown format of score file '%s'", filename);
3318     fclose(file);
3319     return;
3320   }
3321
3322   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3323   {
3324     fscanf(file, "%d", &highscore[i].Score);
3325     fgets(line, MAX_LINE_LEN, file);
3326
3327     if (line[strlen(line) - 1] == '\n')
3328       line[strlen(line) - 1] = '\0';
3329
3330     for (line_ptr = line; *line_ptr; line_ptr++)
3331     {
3332       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3333       {
3334         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3335         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3336         break;
3337       }
3338     }
3339   }
3340
3341   fclose(file);
3342 }
3343
3344 void SaveScore(int nr)
3345 {
3346   int i;
3347   char *filename = getScoreFilename(nr);
3348   FILE *file;
3349
3350   InitScoreDirectory(leveldir_current->subdir);
3351
3352   if (!(file = fopen(filename, MODE_WRITE)))
3353   {
3354     Error(ERR_WARN, "cannot save score for level %d", nr);
3355     return;
3356   }
3357
3358   fprintf(file, "%s\n\n", SCORE_COOKIE);
3359
3360   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3361     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3362
3363   fclose(file);
3364
3365   SetFilePermissions(filename, PERMS_PUBLIC);
3366 }
3367
3368
3369 /* ========================================================================= */
3370 /* setup file functions                                                      */
3371 /* ========================================================================= */
3372
3373 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
3374
3375 /* global setup */
3376 #define SETUP_TOKEN_PLAYER_NAME                 0
3377 #define SETUP_TOKEN_SOUND                       1
3378 #define SETUP_TOKEN_SOUND_LOOPS                 2
3379 #define SETUP_TOKEN_SOUND_MUSIC                 3
3380 #define SETUP_TOKEN_SOUND_SIMPLE                4
3381 #define SETUP_TOKEN_TOONS                       5
3382 #define SETUP_TOKEN_SCROLL_DELAY                6
3383 #define SETUP_TOKEN_SOFT_SCROLLING              7
3384 #define SETUP_TOKEN_FADING                      8
3385 #define SETUP_TOKEN_AUTORECORD                  9
3386 #define SETUP_TOKEN_QUICK_DOORS                 10
3387 #define SETUP_TOKEN_TEAM_MODE                   11
3388 #define SETUP_TOKEN_HANDICAP                    12
3389 #define SETUP_TOKEN_TIME_LIMIT                  13
3390 #define SETUP_TOKEN_FULLSCREEN                  14
3391 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
3392 #define SETUP_TOKEN_GRAPHICS_SET                16
3393 #define SETUP_TOKEN_SOUNDS_SET                  17
3394 #define SETUP_TOKEN_MUSIC_SET                   18
3395 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
3396 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
3397 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
3398
3399 #define NUM_GLOBAL_SETUP_TOKENS                 22
3400
3401 /* editor setup */
3402 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
3403 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
3404 #define SETUP_TOKEN_EDITOR_EL_MORE              2
3405 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
3406 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
3407 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
3408 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
3409 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
3410 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
3411 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
3412 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
3413 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
3414
3415 #define NUM_EDITOR_SETUP_TOKENS                 12
3416
3417 /* shortcut setup */
3418 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
3419 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
3420 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
3421
3422 #define NUM_SHORTCUT_SETUP_TOKENS               3
3423
3424 /* player setup */
3425 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
3426 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
3427 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
3428 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
3429 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
3430 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
3431 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
3432 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
3433 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
3434 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
3435 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
3436 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
3437 #define SETUP_TOKEN_PLAYER_KEY_UP               12
3438 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
3439 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
3440 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
3441
3442 #define NUM_PLAYER_SETUP_TOKENS                 16
3443
3444 /* system setup */
3445 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
3446 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
3447
3448 #define NUM_SYSTEM_SETUP_TOKENS                 2
3449
3450 /* options setup */
3451 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
3452
3453 #define NUM_OPTIONS_SETUP_TOKENS                1
3454
3455
3456 static struct SetupInfo si;
3457 static struct SetupEditorInfo sei;
3458 static struct SetupShortcutInfo ssi;
3459 static struct SetupInputInfo sii;
3460 static struct SetupSystemInfo syi;
3461 static struct OptionInfo soi;
3462
3463 static struct TokenInfo global_setup_tokens[] =
3464 {
3465   { TYPE_STRING, &si.player_name,       "player_name"                   },
3466   { TYPE_SWITCH, &si.sound,             "sound"                         },
3467   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
3468   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
3469   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
3470   { TYPE_SWITCH, &si.toons,             "toons"                         },
3471   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
3472   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
3473   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
3474   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
3475   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
3476   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
3477   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
3478   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
3479   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
3480   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
3481   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
3482   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
3483   { TYPE_STRING, &si.music_set,         "music_set"                     },
3484   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3485   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
3486   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
3487 };
3488
3489 static struct TokenInfo editor_setup_tokens[] =
3490 {
3491   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
3492   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
3493   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
3494   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
3495   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
3496   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
3497   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
3498   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
3499   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
3500   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
3501   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
3502   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
3503 };
3504
3505 static struct TokenInfo shortcut_setup_tokens[] =
3506 {
3507   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
3508   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
3509   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
3510 };
3511
3512 static struct TokenInfo player_setup_tokens[] =
3513 {
3514   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
3515   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
3516   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
3517   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
3518   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
3519   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
3520   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
3521   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
3522   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
3523   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
3524   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
3525   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
3526   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
3527   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
3528   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
3529   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
3530 };
3531
3532 static struct TokenInfo system_setup_tokens[] =
3533 {
3534   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
3535   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3536 };
3537
3538 static struct TokenInfo options_setup_tokens[] =
3539 {
3540   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
3541 };
3542
3543 static char *get_corrected_login_name(char *login_name)
3544 {
3545   /* needed because player name must be a fixed length string */
3546   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3547
3548   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3549   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3550
3551   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
3552     if (strchr(login_name_new, ' '))
3553       *strchr(login_name_new, ' ') = '\0';
3554
3555   return login_name_new;
3556 }
3557
3558 static void setSetupInfoToDefaults(struct SetupInfo *si)
3559 {
3560   int i;
3561
3562   si->player_name = get_corrected_login_name(getLoginName());
3563
3564   si->sound = TRUE;
3565   si->sound_loops = TRUE;
3566   si->sound_music = TRUE;
3567   si->sound_simple = TRUE;
3568   si->toons = TRUE;
3569   si->double_buffering = TRUE;
3570   si->direct_draw = !si->double_buffering;
3571   si->scroll_delay = TRUE;
3572   si->soft_scrolling = TRUE;
3573   si->fading = FALSE;
3574   si->autorecord = TRUE;
3575   si->quick_doors = FALSE;
3576   si->team_mode = FALSE;
3577   si->handicap = TRUE;
3578   si->time_limit = TRUE;
3579   si->fullscreen = FALSE;
3580   si->ask_on_escape = TRUE;
3581
3582   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3583   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3584   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3585   si->override_level_graphics = FALSE;
3586   si->override_level_sounds = FALSE;
3587   si->override_level_music = FALSE;
3588
3589   si->editor.el_boulderdash = TRUE;
3590   si->editor.el_emerald_mine = TRUE;
3591   si->editor.el_more = TRUE;
3592   si->editor.el_sokoban = TRUE;
3593   si->editor.el_supaplex = TRUE;
3594   si->editor.el_diamond_caves = TRUE;
3595   si->editor.el_dx_boulderdash = TRUE;
3596   si->editor.el_chars = TRUE;
3597   si->editor.el_custom = TRUE;
3598   si->editor.el_custom_more = FALSE;
3599
3600   si->editor.el_headlines = TRUE;
3601   si->editor.el_user_defined = FALSE;
3602
3603   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3604   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3605   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3606
3607   for (i = 0; i < MAX_PLAYERS; i++)
3608   {
3609     si->input[i].use_joystick = FALSE;
3610     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3611     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
3612     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3613     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
3614     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
3615     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3616     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
3617     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
3618     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
3619     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
3620     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3621     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
3622     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
3623     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
3624     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
3625   }
3626
3627   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3628   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3629
3630   si->options.verbose = FALSE;
3631 }
3632
3633 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3634 {
3635   int i, pnr;
3636
3637   if (!setup_file_hash)
3638     return;
3639
3640   /* global setup */
3641   si = setup;
3642   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3643     setSetupInfo(global_setup_tokens, i,
3644                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3645   setup = si;
3646
3647   /* editor setup */
3648   sei = setup.editor;
3649   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3650     setSetupInfo(editor_setup_tokens, i,
3651                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3652   setup.editor = sei;
3653
3654   /* shortcut setup */
3655   ssi = setup.shortcut;
3656   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3657     setSetupInfo(shortcut_setup_tokens, i,
3658                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3659   setup.shortcut = ssi;
3660
3661   /* player setup */
3662   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3663   {
3664     char prefix[30];
3665
3666     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3667
3668     sii = setup.input[pnr];
3669     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3670     {
3671       char full_token[100];
3672
3673       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3674       setSetupInfo(player_setup_tokens, i,
3675                    getHashEntry(setup_file_hash, full_token));
3676     }
3677     setup.input[pnr] = sii;
3678   }
3679
3680   /* system setup */
3681   syi = setup.system;
3682   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3683     setSetupInfo(system_setup_tokens, i,
3684                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3685   setup.system = syi;
3686
3687   /* options setup */
3688   soi = setup.options;
3689   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3690     setSetupInfo(options_setup_tokens, i,
3691                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3692   setup.options = soi;
3693 }
3694
3695 void LoadSetup()
3696 {
3697   char *filename = getSetupFilename();
3698   SetupFileHash *setup_file_hash = NULL;
3699
3700   /* always start with reliable default values */
3701   setSetupInfoToDefaults(&setup);
3702
3703   setup_file_hash = loadSetupFileHash(filename);
3704
3705   if (setup_file_hash)
3706   {
3707     char *player_name_new;
3708
3709     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3710     decodeSetupFileHash(setup_file_hash);
3711
3712     setup.direct_draw = !setup.double_buffering;
3713
3714     freeSetupFileHash(setup_file_hash);
3715
3716     /* needed to work around problems with fixed length strings */
3717     player_name_new = get_corrected_login_name(setup.player_name);
3718     free(setup.player_name);
3719     setup.player_name = player_name_new;
3720   }
3721   else
3722     Error(ERR_WARN, "using default setup values");
3723 }
3724
3725 void SaveSetup()
3726 {
3727   char *filename = getSetupFilename();
3728   FILE *file;
3729   int i, pnr;
3730
3731   InitUserDataDirectory();
3732
3733   if (!(file = fopen(filename, MODE_WRITE)))
3734   {
3735     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3736     return;
3737   }
3738
3739   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3740                                                getCookie("SETUP")));
3741   fprintf(file, "\n");
3742
3743   /* global setup */
3744   si = setup;
3745   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3746   {
3747     /* just to make things nicer :) */
3748     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3749         i == SETUP_TOKEN_GRAPHICS_SET)
3750       fprintf(file, "\n");
3751
3752     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3753   }
3754
3755   /* editor setup */
3756   sei = setup.editor;
3757   fprintf(file, "\n");
3758   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3759     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3760
3761   /* shortcut setup */
3762   ssi = setup.shortcut;
3763   fprintf(file, "\n");
3764   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3765     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3766
3767   /* player setup */
3768   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3769   {
3770     char prefix[30];
3771
3772     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3773     fprintf(file, "\n");
3774
3775     sii = setup.input[pnr];
3776     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3777       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3778   }
3779
3780   /* system setup */
3781   syi = setup.system;
3782   fprintf(file, "\n");
3783   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3784     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3785
3786   /* options setup */
3787   soi = setup.options;
3788   fprintf(file, "\n");
3789   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3790     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3791
3792   fclose(file);
3793
3794   SetFilePermissions(filename, PERMS_PRIVATE);
3795 }
3796
3797 void LoadCustomElementDescriptions()
3798 {
3799   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3800   SetupFileHash *setup_file_hash;
3801   int i;
3802
3803   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3804   {
3805     if (element_info[i].custom_description != NULL)
3806     {
3807       free(element_info[i].custom_description);
3808       element_info[i].custom_description = NULL;
3809     }
3810   }
3811
3812   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3813     return;
3814
3815   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3816   {
3817     char *token = getStringCat2(element_info[i].token_name, ".name");
3818     char *value = getHashEntry(setup_file_hash, token);
3819
3820     if (value != NULL)
3821       element_info[i].custom_description = getStringCopy(value);
3822
3823     free(token);
3824   }
3825
3826   freeSetupFileHash(setup_file_hash);
3827 }
3828
3829 void LoadSpecialMenuDesignSettings()
3830 {
3831   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3832   SetupFileHash *setup_file_hash;
3833   int i, j;
3834
3835   /* always start with reliable default values from default config */
3836   for (i = 0; image_config_vars[i].token != NULL; i++)
3837     for (j = 0; image_config[j].token != NULL; j++)
3838       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3839         *image_config_vars[i].value =
3840           get_auto_parameter_value(image_config_vars[i].token,
3841                                    image_config[j].value);
3842
3843   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3844     return;
3845
3846   /* special case: initialize with default values that may be overwritten */
3847   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3848   {
3849     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3850     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3851     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3852
3853     if (value_x != NULL)
3854       menu.draw_xoffset[i] = get_integer_from_string(value_x);
3855     if (value_y != NULL)
3856       menu.draw_yoffset[i] = get_integer_from_string(value_y);
3857     if (list_size != NULL)
3858       menu.list_size[i] = get_integer_from_string(list_size);
3859   }
3860
3861   /* read (and overwrite with) values that may be specified in config file */
3862   for (i = 0; image_config_vars[i].token != NULL; i++)
3863   {
3864     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3865
3866     if (value != NULL)
3867       *image_config_vars[i].value =
3868         get_auto_parameter_value(image_config_vars[i].token, value);
3869   }
3870
3871   freeSetupFileHash(setup_file_hash);
3872 }
3873
3874 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3875 {
3876   char *filename = getEditorSetupFilename();
3877   SetupFileList *setup_file_list, *list;
3878   SetupFileHash *element_hash;
3879   int num_unknown_tokens = 0;
3880   int i;
3881
3882   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3883     return;
3884
3885   element_hash = newSetupFileHash();
3886
3887   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3888     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3889
3890   /* determined size may be larger than needed (due to unknown elements) */
3891   *num_elements = 0;
3892   for (list = setup_file_list; list != NULL; list = list->next)
3893     (*num_elements)++;
3894
3895   /* add space for up to 3 more elements for padding that may be needed */
3896   *num_elements += 3;
3897
3898   *elements = checked_malloc(*num_elements * sizeof(int));
3899
3900   *num_elements = 0;
3901   for (list = setup_file_list; list != NULL; list = list->next)
3902   {
3903     char *value = getHashEntry(element_hash, list->token);
3904
3905     if (value)
3906     {
3907       (*elements)[(*num_elements)++] = atoi(value);
3908     }
3909     else
3910     {
3911       if (num_unknown_tokens == 0)
3912       {
3913         Error(ERR_RETURN_LINE, "-");
3914         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3915         Error(ERR_RETURN, "- config file: '%s'", filename);
3916
3917         num_unknown_tokens++;
3918       }
3919
3920       Error(ERR_RETURN, "- token: '%s'", list->token);
3921     }
3922   }
3923
3924   if (num_unknown_tokens > 0)
3925     Error(ERR_RETURN_LINE, "-");
3926
3927   while (*num_elements % 4)     /* pad with empty elements, if needed */
3928     (*elements)[(*num_elements)++] = EL_EMPTY;
3929
3930   freeSetupFileList(setup_file_list);
3931   freeSetupFileHash(element_hash);
3932
3933 #if 0
3934   /* TEST-ONLY */
3935   for (i = 0; i < *num_elements; i++)
3936     printf("editor: element '%s' [%d]\n",
3937            element_info[(*elements)[i]].token_name, (*elements)[i]);
3938 #endif
3939 }
3940
3941 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3942                                                      boolean is_sound)
3943 {
3944   SetupFileHash *setup_file_hash = NULL;
3945   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3946   char *filename_music, *filename_prefix, *filename_info;
3947   struct
3948   {
3949     char *token;
3950     char **value_ptr;
3951   }
3952   token_to_value_ptr[] =
3953   {
3954     { "title_header",   &tmp_music_file_info.title_header       },
3955     { "artist_header",  &tmp_music_file_info.artist_header      },
3956     { "album_header",   &tmp_music_file_info.album_header       },
3957     { "year_header",    &tmp_music_file_info.year_header        },
3958
3959     { "title",          &tmp_music_file_info.title              },
3960     { "artist",         &tmp_music_file_info.artist             },
3961     { "album",          &tmp_music_file_info.album              },
3962     { "year",           &tmp_music_file_info.year               },
3963
3964     { NULL,             NULL                                    },
3965   };
3966   int i;
3967
3968   filename_music = (is_sound ? getCustomSoundFilename(basename) :
3969                     getCustomMusicFilename(basename));
3970
3971   if (filename_music == NULL)
3972     return NULL;
3973
3974   /* ---------- try to replace file extension ---------- */
3975
3976   filename_prefix = getStringCopy(filename_music);
3977   if (strrchr(filename_prefix, '.') != NULL)
3978     *strrchr(filename_prefix, '.') = '\0';
3979   filename_info = getStringCat2(filename_prefix, ".txt");
3980
3981 #if 0
3982   printf("trying to load file '%s'...\n", filename_info);
3983 #endif
3984
3985   if (fileExists(filename_info))
3986     setup_file_hash = loadSetupFileHash(filename_info);
3987
3988   free(filename_prefix);
3989   free(filename_info);
3990
3991   if (setup_file_hash == NULL)
3992   {
3993     /* ---------- try to add file extension ---------- */
3994
3995     filename_prefix = getStringCopy(filename_music);
3996     filename_info = getStringCat2(filename_prefix, ".txt");
3997
3998 #if 0
3999     printf("trying to load file '%s'...\n", filename_info);
4000 #endif
4001
4002     if (fileExists(filename_info))
4003       setup_file_hash = loadSetupFileHash(filename_info);
4004
4005     free(filename_prefix);
4006     free(filename_info);
4007   }
4008
4009   if (setup_file_hash == NULL)
4010     return NULL;
4011
4012   /* ---------- music file info found ---------- */
4013
4014   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4015
4016   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4017   {
4018     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4019
4020     *token_to_value_ptr[i].value_ptr =
4021       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4022   }
4023
4024   tmp_music_file_info.basename = getStringCopy(basename);
4025   tmp_music_file_info.music = music;
4026   tmp_music_file_info.is_sound = is_sound;
4027
4028   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4029   *new_music_file_info = tmp_music_file_info;
4030
4031   return new_music_file_info;
4032 }
4033
4034 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4035 {
4036   return get_music_file_info_ext(basename, music, FALSE);
4037 }
4038
4039 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4040 {
4041   return get_music_file_info_ext(basename, sound, TRUE);
4042 }
4043
4044 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4045                                      char *basename, boolean is_sound)
4046 {
4047   for (; list != NULL; list = list->next)
4048     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4049       return TRUE;
4050
4051   return FALSE;
4052 }
4053
4054 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4055 {
4056   return music_info_listed_ext(list, basename, FALSE);
4057 }
4058
4059 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4060 {
4061   return music_info_listed_ext(list, basename, TRUE);
4062 }
4063
4064 void LoadMusicInfo()
4065 {
4066   char *music_directory = getCustomMusicDirectory();
4067   int num_music = getMusicListSize();
4068   int num_music_noconf = 0;
4069   int num_sounds = getSoundListSize();
4070   DIR *dir;
4071   struct dirent *dir_entry;
4072   struct FileInfo *music, *sound;
4073   struct MusicFileInfo *next, **new;
4074   int i;
4075
4076   while (music_file_info != NULL)
4077   {
4078     next = music_file_info->next;
4079
4080     checked_free(music_file_info->basename);
4081
4082     checked_free(music_file_info->title_header);
4083     checked_free(music_file_info->artist_header);
4084     checked_free(music_file_info->album_header);
4085     checked_free(music_file_info->year_header);
4086
4087     checked_free(music_file_info->title);
4088     checked_free(music_file_info->artist);
4089     checked_free(music_file_info->album);
4090     checked_free(music_file_info->year);
4091
4092     free(music_file_info);
4093
4094     music_file_info = next;
4095   }
4096
4097   new = &music_file_info;
4098
4099 #if 0
4100   printf("::: num_music == %d\n", num_music);
4101 #endif
4102
4103   for (i = 0; i < num_music; i++)
4104   {
4105     music = getMusicListEntry(i);
4106
4107 #if 0
4108     printf("::: %d [%08x]\n", i, music->filename);
4109 #endif
4110
4111     if (music->filename == NULL)
4112       continue;
4113
4114     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4115       continue;
4116
4117     /* a configured file may be not recognized as music */
4118     if (!FileIsMusic(music->filename))
4119       continue;
4120
4121 #if 0
4122     printf("::: -> '%s' (configured)\n", music->filename);
4123 #endif
4124
4125     if (!music_info_listed(music_file_info, music->filename))
4126     {
4127       *new = get_music_file_info(music->filename, i);
4128       if (*new != NULL)
4129         new = &(*new)->next;
4130     }
4131   }
4132
4133   if ((dir = opendir(music_directory)) == NULL)
4134   {
4135     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4136     return;
4137   }
4138
4139   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
4140   {
4141     char *basename = dir_entry->d_name;
4142     boolean music_already_used = FALSE;
4143     int i;
4144
4145     /* skip all music files that are configured in music config file */
4146     for (i = 0; i < num_music; i++)
4147     {
4148       music = getMusicListEntry(i);
4149
4150       if (music->filename == NULL)
4151         continue;
4152
4153       if (strcmp(basename, music->filename) == 0)
4154       {
4155         music_already_used = TRUE;
4156         break;
4157       }
4158     }
4159
4160     if (music_already_used)
4161       continue;
4162
4163     if (!FileIsMusic(basename))
4164       continue;
4165
4166 #if 0
4167     printf("::: -> '%s' (found in directory)\n", basename);
4168 #endif
4169
4170     if (!music_info_listed(music_file_info, basename))
4171     {
4172       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4173       if (*new != NULL)
4174         new = &(*new)->next;
4175     }
4176
4177     num_music_noconf++;
4178   }
4179
4180   closedir(dir);
4181
4182   for (i = 0; i < num_sounds; i++)
4183   {
4184     sound = getSoundListEntry(i);
4185
4186     if (sound->filename == NULL)
4187       continue;
4188
4189     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4190       continue;
4191
4192     /* a configured file may be not recognized as sound */
4193     if (!FileIsSound(sound->filename))
4194       continue;
4195
4196 #if 0
4197     printf("::: -> '%s' (configured)\n", sound->filename);
4198 #endif
4199
4200     if (!sound_info_listed(music_file_info, sound->filename))
4201     {
4202       *new = get_sound_file_info(sound->filename, i);
4203       if (*new != NULL)
4204         new = &(*new)->next;
4205     }
4206   }
4207
4208 #if 0
4209   /* TEST-ONLY */
4210   for (next = music_file_info; next != NULL; next = next->next)
4211     printf("::: title == '%s'\n", next->title);
4212 #endif
4213 }
4214
4215 void add_helpanim_entry(int element, int action, int direction, int delay,
4216                         int *num_list_entries)
4217 {
4218   struct HelpAnimInfo *new_list_entry;
4219   (*num_list_entries)++;
4220
4221   helpanim_info =
4222     checked_realloc(helpanim_info,
4223                     *num_list_entries * sizeof(struct HelpAnimInfo));
4224   new_list_entry = &helpanim_info[*num_list_entries - 1];
4225
4226   new_list_entry->element = element;
4227   new_list_entry->action = action;
4228   new_list_entry->direction = direction;
4229   new_list_entry->delay = delay;
4230 }
4231
4232 void print_unknown_token(char *filename, char *token, int token_nr)
4233 {
4234   if (token_nr == 0)
4235   {
4236     Error(ERR_RETURN_LINE, "-");
4237     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4238     Error(ERR_RETURN, "- config file: '%s'", filename);
4239   }
4240
4241   Error(ERR_RETURN, "- token: '%s'", token);
4242 }
4243
4244 void print_unknown_token_end(int token_nr)
4245 {
4246   if (token_nr > 0)
4247     Error(ERR_RETURN_LINE, "-");
4248 }
4249
4250 void LoadHelpAnimInfo()
4251 {
4252   char *filename = getHelpAnimFilename();
4253   SetupFileList *setup_file_list = NULL, *list;
4254   SetupFileHash *element_hash, *action_hash, *direction_hash;
4255   int num_list_entries = 0;
4256   int num_unknown_tokens = 0;
4257   int i;
4258
4259   if (fileExists(filename))
4260     setup_file_list = loadSetupFileList(filename);
4261
4262   if (setup_file_list == NULL)
4263   {
4264     /* use reliable default values from static configuration */
4265     SetupFileList *insert_ptr;
4266
4267     insert_ptr = setup_file_list =
4268       newSetupFileList(helpanim_config[0].token,
4269                        helpanim_config[0].value);
4270
4271     for (i = 1; helpanim_config[i].token; i++)
4272       insert_ptr = addListEntry(insert_ptr,
4273                                 helpanim_config[i].token,
4274                                 helpanim_config[i].value);
4275   }
4276
4277   element_hash   = newSetupFileHash();
4278   action_hash    = newSetupFileHash();
4279   direction_hash = newSetupFileHash();
4280
4281   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4282     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4283
4284   for (i = 0; i < NUM_ACTIONS; i++)
4285     setHashEntry(action_hash, element_action_info[i].suffix,
4286                  i_to_a(element_action_info[i].value));
4287
4288   /* do not store direction index (bit) here, but direction value! */
4289   for (i = 0; i < NUM_DIRECTIONS; i++)
4290     setHashEntry(direction_hash, element_direction_info[i].suffix,
4291                  i_to_a(1 << element_direction_info[i].value));
4292
4293   for (list = setup_file_list; list != NULL; list = list->next)
4294   {
4295     char *element_token, *action_token, *direction_token;
4296     char *element_value, *action_value, *direction_value;
4297     int delay = atoi(list->value);
4298
4299     if (strcmp(list->token, "end") == 0)
4300     {
4301       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4302
4303       continue;
4304     }
4305
4306     /* first try to break element into element/action/direction parts;
4307        if this does not work, also accept combined "element[.act][.dir]"
4308        elements (like "dynamite.active"), which are unique elements */
4309
4310     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
4311     {
4312       element_value = getHashEntry(element_hash, list->token);
4313       if (element_value != NULL)        /* element found */
4314         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4315                            &num_list_entries);
4316       else
4317       {
4318         /* no further suffixes found -- this is not an element */
4319         print_unknown_token(filename, list->token, num_unknown_tokens++);
4320       }
4321
4322       continue;
4323     }
4324
4325     /* token has format "<prefix>.<something>" */
4326
4327     action_token = strchr(list->token, '.');    /* suffix may be action ... */
4328     direction_token = action_token;             /* ... or direction */
4329
4330     element_token = getStringCopy(list->token);
4331     *strchr(element_token, '.') = '\0';
4332
4333     element_value = getHashEntry(element_hash, element_token);
4334
4335     if (element_value == NULL)          /* this is no element */
4336     {
4337       element_value = getHashEntry(element_hash, list->token);
4338       if (element_value != NULL)        /* combined element found */
4339         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4340                            &num_list_entries);
4341       else
4342         print_unknown_token(filename, list->token, num_unknown_tokens++);
4343
4344       free(element_token);
4345
4346       continue;
4347     }
4348
4349     action_value = getHashEntry(action_hash, action_token);
4350
4351     if (action_value != NULL)           /* action found */
4352     {
4353       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4354                     &num_list_entries);
4355
4356       free(element_token);
4357
4358       continue;
4359     }
4360
4361     direction_value = getHashEntry(direction_hash, direction_token);
4362
4363     if (direction_value != NULL)        /* direction found */
4364     {
4365       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4366                          &num_list_entries);
4367
4368       free(element_token);
4369
4370       continue;
4371     }
4372
4373     if (strchr(action_token + 1, '.') == NULL)
4374     {
4375       /* no further suffixes found -- this is not an action nor direction */
4376
4377       element_value = getHashEntry(element_hash, list->token);
4378       if (element_value != NULL)        /* combined element found */
4379         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4380                            &num_list_entries);
4381       else
4382         print_unknown_token(filename, list->token, num_unknown_tokens++);
4383
4384       free(element_token);
4385
4386       continue;
4387     }
4388
4389     /* token has format "<prefix>.<suffix>.<something>" */
4390
4391     direction_token = strchr(action_token + 1, '.');
4392
4393     action_token = getStringCopy(action_token);
4394     *strchr(action_token + 1, '.') = '\0';
4395
4396     action_value = getHashEntry(action_hash, action_token);
4397
4398     if (action_value == NULL)           /* this is no action */
4399     {
4400       element_value = getHashEntry(element_hash, list->token);
4401       if (element_value != NULL)        /* combined element found */
4402         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4403                            &num_list_entries);
4404       else
4405         print_unknown_token(filename, list->token, num_unknown_tokens++);
4406
4407       free(element_token);
4408       free(action_token);
4409
4410       continue;
4411     }
4412
4413     direction_value = getHashEntry(direction_hash, direction_token);
4414
4415     if (direction_value != NULL)        /* direction found */
4416     {
4417       add_helpanim_entry(atoi(element_value), atoi(action_value),
4418                          atoi(direction_value), delay, &num_list_entries);
4419
4420       free(element_token);
4421       free(action_token);
4422
4423       continue;
4424     }
4425
4426     /* this is no direction */
4427
4428     element_value = getHashEntry(element_hash, list->token);
4429     if (element_value != NULL)          /* combined element found */
4430       add_helpanim_entry(atoi(element_value), -1, -1, delay,
4431                          &num_list_entries);
4432     else
4433       print_unknown_token(filename, list->token, num_unknown_tokens++);
4434
4435     free(element_token);
4436     free(action_token);
4437   }
4438
4439   print_unknown_token_end(num_unknown_tokens);
4440
4441   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4442   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
4443
4444   freeSetupFileList(setup_file_list);
4445   freeSetupFileHash(element_hash);
4446   freeSetupFileHash(action_hash);
4447   freeSetupFileHash(direction_hash);
4448
4449 #if 0
4450   /* TEST ONLY */
4451   for (i = 0; i < num_list_entries; i++)
4452     printf("::: %d, %d, %d => %d\n",
4453            helpanim_info[i].element,
4454            helpanim_info[i].action,
4455            helpanim_info[i].direction,
4456            helpanim_info[i].delay);
4457 #endif
4458 }
4459
4460 void LoadHelpTextInfo()
4461 {
4462   char *filename = getHelpTextFilename();
4463   int i;
4464
4465   if (helptext_info != NULL)
4466   {
4467     freeSetupFileHash(helptext_info);
4468     helptext_info = NULL;
4469   }
4470
4471   if (fileExists(filename))
4472     helptext_info = loadSetupFileHash(filename);
4473
4474   if (helptext_info == NULL)
4475   {
4476     /* use reliable default values from static configuration */
4477     helptext_info = newSetupFileHash();
4478
4479     for (i = 0; helptext_config[i].token; i++)
4480       setHashEntry(helptext_info,
4481                    helptext_config[i].token,
4482                    helptext_config[i].value);
4483   }
4484
4485 #if 0
4486   /* TEST ONLY */
4487   BEGIN_HASH_ITERATION(helptext_info, itr)
4488   {
4489     printf("::: '%s' => '%s'\n",
4490            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4491   }
4492   END_HASH_ITERATION(hash, itr)
4493 #endif
4494 }