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