a6b41e1ba640f2a226a87288ce45b98304be3971
[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_level_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_level_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       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1202       fclose(file);
1203       return;
1204     }
1205   }
1206   else  /* check for pre-2.0 file format with cookie string */
1207   {
1208     strcpy(cookie, chunk_name);
1209     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1210     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1211       cookie[strlen(cookie) - 1] = '\0';
1212
1213     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1214     {
1215       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1216       fclose(file);
1217       return;
1218     }
1219
1220     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1221     {
1222       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1223       fclose(file);
1224       return;
1225     }
1226
1227     /* pre-2.0 level files have no game version, so use file version here */
1228     level->game_version = level->file_version;
1229   }
1230
1231   if (level->file_version < FILE_VERSION_1_2)
1232   {
1233     /* level files from versions before 1.2.0 without chunk structure */
1234     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
1235     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1236   }
1237   else
1238   {
1239     static struct
1240     {
1241       char *name;
1242       int size;
1243       int (*loader)(FILE *, int, struct LevelInfo *);
1244     }
1245     chunk_info[] =
1246     {
1247       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
1248       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
1249       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
1250       { "BODY", -1,                     LoadLevel_BODY },
1251       { "CONT", -1,                     LoadLevel_CONT },
1252       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
1253       { "CNT3", -1,                     LoadLevel_CNT3 },
1254       { "CUS1", -1,                     LoadLevel_CUS1 },
1255       { "CUS2", -1,                     LoadLevel_CUS2 },
1256       { "CUS3", -1,                     LoadLevel_CUS3 },
1257       { "CUS4", -1,                     LoadLevel_CUS4 },
1258       { "GRP1", -1,                     LoadLevel_GRP1 },
1259       {  NULL,  0,                      NULL }
1260     };
1261
1262     while (getFileChunkBE(file, chunk_name, &chunk_size))
1263     {
1264       int i = 0;
1265
1266       while (chunk_info[i].name != NULL &&
1267              strcmp(chunk_name, chunk_info[i].name) != 0)
1268         i++;
1269
1270       if (chunk_info[i].name == NULL)
1271       {
1272         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1273               chunk_name, filename);
1274         ReadUnusedBytesFromFile(file, chunk_size);
1275       }
1276       else if (chunk_info[i].size != -1 &&
1277                chunk_info[i].size != chunk_size)
1278       {
1279         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1280               chunk_size, chunk_name, filename);
1281         ReadUnusedBytesFromFile(file, chunk_size);
1282       }
1283       else
1284       {
1285         /* call function to load this level chunk */
1286         int chunk_size_expected =
1287           (chunk_info[i].loader)(file, chunk_size, level);
1288
1289         /* the size of some chunks cannot be checked before reading other
1290            chunks first (like "HEAD" and "BODY") that contain some header
1291            information, so check them here */
1292         if (chunk_size_expected != chunk_size)
1293         {
1294           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1295                 chunk_size, chunk_name, filename);
1296         }
1297       }
1298     }
1299   }
1300
1301   fclose(file);
1302 }
1303
1304 /* ------------------------------------------------------------------------- */
1305 /* functions for loading EM level                                            */
1306 /* ------------------------------------------------------------------------- */
1307
1308 static int map_em_element_yam(int element)
1309 {
1310   switch (element)
1311   {
1312     case 0x00:  return EL_EMPTY;
1313     case 0x01:  return EL_EMERALD;
1314     case 0x02:  return EL_DIAMOND;
1315     case 0x03:  return EL_ROCK;
1316     case 0x04:  return EL_ROBOT;
1317     case 0x05:  return EL_SPACESHIP_UP;
1318     case 0x06:  return EL_BOMB;
1319     case 0x07:  return EL_BUG_UP;
1320     case 0x08:  return EL_AMOEBA_DROP;
1321     case 0x09:  return EL_NUT;
1322     case 0x0a:  return EL_YAMYAM;
1323     case 0x0b:  return EL_QUICKSAND_FULL;
1324     case 0x0c:  return EL_SAND;
1325     case 0x0d:  return EL_WALL_SLIPPERY;
1326     case 0x0e:  return EL_STEELWALL;
1327     case 0x0f:  return EL_WALL;
1328     case 0x10:  return EL_EM_KEY_1;
1329     case 0x11:  return EL_EM_KEY_2;
1330     case 0x12:  return EL_EM_KEY_4;
1331     case 0x13:  return EL_EM_KEY_3;
1332     case 0x14:  return EL_MAGIC_WALL;
1333     case 0x15:  return EL_ROBOT_WHEEL;
1334     case 0x16:  return EL_DYNAMITE;
1335
1336     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
1337     case 0x18:  return EL_BUG_UP;                       /* EMC */
1338     case 0x1a:  return EL_DIAMOND;                      /* EMC */
1339     case 0x1b:  return EL_EMERALD;                      /* EMC */
1340     case 0x25:  return EL_NUT;                          /* EMC */
1341     case 0x80:  return EL_EMPTY;                        /* EMC */
1342     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
1343     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
1344     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
1345     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
1346     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
1347     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
1348     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
1349     case 0xbd:  return EL_SAND;                         /* EMC */
1350
1351     default:
1352       Error(ERR_WARN, "invalid level element %d", element);
1353       return EL_UNKNOWN;
1354   }
1355 }
1356
1357 static int map_em_element_field(int element)
1358 {
1359   if (element >= 0xc8 && element <= 0xe1)
1360     return EL_CHAR_A + (element - 0xc8);
1361   else if (element >= 0xe2 && element <= 0xeb)
1362     return EL_CHAR_0 + (element - 0xe2);
1363
1364   switch (element)
1365   {
1366     case 0x00:  return EL_ROCK;
1367     case 0x01:  return EL_ROCK;                         /* EMC */
1368     case 0x02:  return EL_DIAMOND;
1369     case 0x03:  return EL_DIAMOND;
1370     case 0x04:  return EL_ROBOT;
1371     case 0x05:  return EL_ROBOT;                        /* EMC */
1372     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
1373     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
1374     case 0x08:  return EL_SPACESHIP_UP;
1375     case 0x09:  return EL_SPACESHIP_RIGHT;
1376     case 0x0a:  return EL_SPACESHIP_DOWN;
1377     case 0x0b:  return EL_SPACESHIP_LEFT;
1378     case 0x0c:  return EL_SPACESHIP_UP;
1379     case 0x0d:  return EL_SPACESHIP_RIGHT;
1380     case 0x0e:  return EL_SPACESHIP_DOWN;
1381     case 0x0f:  return EL_SPACESHIP_LEFT;
1382
1383     case 0x10:  return EL_BOMB;
1384     case 0x11:  return EL_BOMB;                         /* EMC */
1385     case 0x12:  return EL_EMERALD;
1386     case 0x13:  return EL_EMERALD;
1387     case 0x14:  return EL_BUG_UP;
1388     case 0x15:  return EL_BUG_RIGHT;
1389     case 0x16:  return EL_BUG_DOWN;
1390     case 0x17:  return EL_BUG_LEFT;
1391     case 0x18:  return EL_BUG_UP;
1392     case 0x19:  return EL_BUG_RIGHT;
1393     case 0x1a:  return EL_BUG_DOWN;
1394     case 0x1b:  return EL_BUG_LEFT;
1395     case 0x1c:  return EL_AMOEBA_DROP;
1396     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
1397     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
1398     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
1399
1400     case 0x20:  return EL_ROCK;
1401     case 0x21:  return EL_BOMB;                         /* EMC */
1402     case 0x22:  return EL_DIAMOND;                      /* EMC */
1403     case 0x23:  return EL_EMERALD;                      /* EMC */
1404     case 0x24:  return EL_MAGIC_WALL;
1405     case 0x25:  return EL_NUT;
1406     case 0x26:  return EL_NUT;                          /* EMC */
1407     case 0x27:  return EL_NUT;                          /* EMC */
1408
1409       /* looks like magic wheel, but is _always_ activated */
1410     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
1411
1412     case 0x29:  return EL_YAMYAM;       /* up */
1413     case 0x2a:  return EL_YAMYAM;       /* down */
1414     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
1415     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
1416     case 0x2d:  return EL_QUICKSAND_FULL;
1417     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
1418     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
1419
1420     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
1421     case 0x31:  return EL_SAND;                         /* EMC */
1422     case 0x32:  return EL_SAND;                         /* EMC */
1423     case 0x33:  return EL_SAND;                         /* EMC */
1424     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
1425     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
1426     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
1427     case 0x37:  return EL_SAND;                         /* EMC */
1428     case 0x38:  return EL_ROCK;                         /* EMC */
1429     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
1430     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
1431     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
1432     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
1433     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
1434     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
1435     case 0x3f:  return EL_ACID_POOL_BOTTOM;
1436
1437     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
1438     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
1439     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
1440     case 0x43:  return EL_BALLOON;                      /* EMC */
1441     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
1442     case 0x45:  return EL_SPRING;                       /* EMC */
1443     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
1444     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
1445     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
1446     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
1447     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
1448     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
1449     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
1450     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
1451     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
1452     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
1453
1454     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
1455     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
1456     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
1457     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
1458     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
1459     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
1460     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
1461     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
1462     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
1463     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
1464     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
1465     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
1466     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
1467     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
1468     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
1469     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
1470
1471     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
1472     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
1473     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
1474     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
1475     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
1476     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
1477     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
1478     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
1479     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
1480     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
1481     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
1482     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
1483     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
1484     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
1485     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
1486     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
1487
1488     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
1489     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
1490     case 0x72:  return EL_NUT;          /* left */      /* EMC */
1491     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
1492     case 0x74:  return EL_STEELWALL;
1493     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
1494     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
1495     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
1496     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
1497     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
1498     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
1499     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
1500     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
1501     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
1502     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
1503     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
1504
1505     case 0x80:  return EL_EMPTY;
1506     case 0x81:  return EL_WALL_SLIPPERY;
1507     case 0x82:  return EL_SAND;
1508     case 0x83:  return EL_STEELWALL;
1509     case 0x84:  return EL_WALL;
1510     case 0x85:  return EL_EM_KEY_1;
1511     case 0x86:  return EL_EM_KEY_2;
1512     case 0x87:  return EL_EM_KEY_4;
1513     case 0x88:  return EL_EM_KEY_3;
1514     case 0x89:  return EL_EM_GATE_1;
1515     case 0x8a:  return EL_EM_GATE_2;
1516     case 0x8b:  return EL_EM_GATE_4;
1517     case 0x8c:  return EL_EM_GATE_3;
1518     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
1519     case 0x8e:  return EL_EM_GATE_1_GRAY;
1520     case 0x8f:  return EL_EM_GATE_2_GRAY;
1521
1522     case 0x90:  return EL_EM_GATE_4_GRAY;
1523     case 0x91:  return EL_EM_GATE_3_GRAY;
1524     case 0x92:  return EL_MAGIC_WALL;
1525     case 0x93:  return EL_ROBOT_WHEEL;
1526     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
1527     case 0x95:  return EL_ACID_POOL_TOPLEFT;
1528     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
1529     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
1530     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
1531     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
1532     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
1533     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
1534     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
1535     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
1536     case 0x9e:  return EL_EXIT_CLOSED;
1537     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
1538
1539       /* looks like normal sand, but behaves like wall */
1540     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1541     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
1542     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
1543     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
1544     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1545     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
1546     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
1547     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
1548     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
1549     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
1550     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
1551     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
1552     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
1553     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
1554     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
1555     case 0xaf:  return EL_DYNAMITE;
1556
1557     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
1558     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
1559     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
1560     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
1561     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
1562     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
1563     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
1564     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
1565     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
1566     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
1567     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
1568     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
1569     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
1570     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
1571     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
1572     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
1573
1574     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
1575     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
1576     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
1577     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
1578     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
1579     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
1580     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
1581     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
1582
1583       /* characters: see above */
1584
1585     case 0xec:  return EL_CHAR_PERIOD;
1586     case 0xed:  return EL_CHAR_EXCLAM;
1587     case 0xee:  return EL_CHAR_COLON;
1588     case 0xef:  return EL_CHAR_QUESTION;
1589
1590     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
1591     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
1592     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
1593     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
1594     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
1595     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
1596     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
1597     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
1598
1599     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
1600     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
1601     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
1602     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
1603     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
1604     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
1605
1606     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
1607     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
1608
1609     default:
1610       /* should never happen (all 8-bit value cases should be handled) */
1611       Error(ERR_WARN, "invalid level element %d", element);
1612       return EL_UNKNOWN;
1613   }
1614 }
1615
1616 #define EM_LEVEL_SIZE                   2106
1617 #define EM_LEVEL_XSIZE                  64
1618 #define EM_LEVEL_YSIZE                  32
1619
1620 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1621                                      struct LevelFileInfo *level_file_info)
1622 {
1623   char *filename = level_file_info->filename;
1624   FILE *file;
1625   unsigned char leveldata[EM_LEVEL_SIZE];
1626   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1627   unsigned char code0 = 0x65;
1628   unsigned char code1 = 0x11;
1629   boolean level_is_crypted = FALSE;
1630   int nr = level_file_info->nr;
1631   int i, x, y;
1632
1633   if (!(file = fopen(filename, MODE_READ)))
1634   {
1635     level->no_level_file = TRUE;
1636
1637     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1638
1639     return;
1640   }
1641
1642   for(i = 0; i < EM_LEVEL_SIZE; i++)
1643     leveldata[i] = fgetc(file);
1644
1645   fclose(file);
1646
1647   /* check if level data is crypted by testing against known starting bytes
1648      of the few existing crypted level files (from Emerald Mine 1 + 2) */
1649
1650   if ((leveldata[0] == 0xf1 ||
1651        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1652   {
1653     level_is_crypted = TRUE;
1654
1655     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
1656       leveldata[0] = 0xf1;
1657   }
1658
1659   if (level_is_crypted)         /* decode crypted level data */
1660   {
1661     for(i = 0; i < EM_LEVEL_SIZE; i++)
1662     {
1663       leveldata[i] ^= code0;
1664       leveldata[i] -= code1;
1665
1666       code0  = (code0 + 7) & 0xff;
1667     }
1668   }
1669
1670   level->fieldx = EM_LEVEL_XSIZE;
1671   level->fieldy = EM_LEVEL_YSIZE;
1672
1673   level->time           = header[46] * 10;
1674   level->gems_needed    = header[47];
1675
1676   /* The original Emerald Mine levels have their level number stored
1677      at the second byte of the level file...
1678      Do not trust this information at other level files, e.g. EMC,
1679      but correct it anyway (normally the first row is completely
1680      steel wall, so the correction does not hurt anyway). */
1681
1682   if (leveldata[1] == nr)
1683     leveldata[1] = leveldata[2];        /* correct level number field */
1684
1685   sprintf(level->name, "Level %d", nr);         /* set level name */
1686
1687   level->score[SC_EMERALD]      = header[36];
1688   level->score[SC_DIAMOND]      = header[37];
1689   level->score[SC_ROBOT]        = header[38];
1690   level->score[SC_SPACESHIP]    = header[39];
1691   level->score[SC_BUG]          = header[40];
1692   level->score[SC_YAMYAM]       = header[41];
1693   level->score[SC_NUT]          = header[42];
1694   level->score[SC_DYNAMITE]     = header[43];
1695   level->score[SC_TIME_BONUS]   = header[44];
1696
1697   level->num_yamyam_contents = 4;
1698
1699   for(i = 0; i < level->num_yamyam_contents; i++)
1700     for(y = 0; y < 3; y++)
1701       for(x = 0; x < 3; x++)
1702         level->yamyam_content[i][x][y] =
1703           map_em_element_yam(header[i * 9 + y * 3 + x]);
1704
1705   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
1706   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
1707   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
1708   level->amoeba_content         = EL_DIAMOND;
1709
1710   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1711   {
1712     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1713
1714     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1715       new_element = EL_AMOEBA_WET;
1716
1717     level->field[x][y] = new_element;
1718   }
1719
1720   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1721   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1722   level->field[x][y] = EL_PLAYER_1;
1723
1724   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1725   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1726   level->field[x][y] = EL_PLAYER_2;
1727 }
1728
1729 /* ------------------------------------------------------------------------- */
1730 /* functions for loading SP level                                            */
1731 /* ------------------------------------------------------------------------- */
1732
1733 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1734 #define SP_LEVEL_SIZE                   1536
1735 #define SP_LEVEL_XSIZE                  60
1736 #define SP_LEVEL_YSIZE                  24
1737 #define SP_LEVEL_NAME_LEN               23
1738
1739 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1740                                        int nr)
1741 {
1742   int i, x, y;
1743
1744   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1745   for (y = 0; y < SP_LEVEL_YSIZE; y++)
1746   {
1747     for (x = 0; x < SP_LEVEL_XSIZE; x++)
1748     {
1749       int element_old = fgetc(file);
1750       int element_new;
1751
1752       if (element_old <= 0x27)
1753         element_new = getMappedElement(EL_SP_START + element_old);
1754       else if (element_old == 0x28)
1755         element_new = EL_INVISIBLE_WALL;
1756       else
1757       {
1758         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1759         Error(ERR_WARN, "invalid level element %d", element_old);
1760
1761         element_new = EL_UNKNOWN;
1762       }
1763
1764       level->field[x][y] = element_new;
1765     }
1766   }
1767
1768   ReadUnusedBytesFromFile(file, 4);
1769
1770   /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1771   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1772
1773   ReadUnusedBytesFromFile(file, 1);
1774
1775   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1776   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1777     level->name[i] = fgetc(file);
1778   level->name[SP_LEVEL_NAME_LEN] = '\0';
1779
1780   /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1781   ReadUnusedBytesFromFile(file, 1);     /* !!! NOT SUPPORTED YET !!! */
1782
1783   /* number of infotrons needed; 0 means that Supaplex will count the total
1784      amount of infotrons in the level and use the low byte of that number.
1785      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1786   level->gems_needed = fgetc(file);
1787
1788   /* information about special gravity port entries */
1789   ReadUnusedBytesFromFile(file, 65);    /* !!! NOT SUPPORTED YET !!! */
1790
1791   level->fieldx = SP_LEVEL_XSIZE;
1792   level->fieldy = SP_LEVEL_YSIZE;
1793
1794   level->time = 0;                      /* no time limit */
1795   level->amoeba_speed = 0;
1796   level->time_magic_wall = 0;
1797   level->time_wheel = 0;
1798   level->amoeba_content = EL_EMPTY;
1799
1800   for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1801     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
1802
1803   /* there are no yamyams in supaplex levels */
1804   for(i = 0; i < level->num_yamyam_contents; i++)
1805     for(y = 0; y < 3; y++)
1806       for(x = 0; x < 3; x++)
1807         level->yamyam_content[i][x][y] = EL_EMPTY;
1808 }
1809
1810 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1811                                      struct LevelFileInfo *level_file_info)
1812 {
1813   char *filename = level_file_info->filename;
1814   FILE *file;
1815   int nr = level_file_info->nr - leveldir_current->first_level;
1816   int i, l, x, y;
1817   char name_first, name_last;
1818   struct LevelInfo multipart_level;
1819   int multipart_xpos, multipart_ypos;
1820   boolean is_multipart_level;
1821   boolean is_first_part;
1822   boolean reading_multipart_level = FALSE;
1823   boolean use_empty_level = FALSE;
1824
1825   if (!(file = fopen(filename, MODE_READ)))
1826   {
1827     level->no_level_file = TRUE;
1828
1829     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1830
1831     return;
1832   }
1833
1834   /* position file stream to the requested level inside the level package */
1835   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1836   {
1837     level->no_level_file = TRUE;
1838
1839     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1840
1841     return;
1842   }
1843
1844   /* there exist Supaplex level package files with multi-part levels which
1845      can be detected as follows: instead of leading and trailing dashes ('-')
1846      to pad the level name, they have leading and trailing numbers which are
1847      the x and y coordinations of the current part of the multi-part level;
1848      if there are '?' characters instead of numbers on the left or right side
1849      of the level name, the multi-part level consists of only horizontal or
1850      vertical parts */
1851
1852   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1853   {
1854     LoadLevelFromFileStream_SP(file, level, l);
1855
1856     /* check if this level is a part of a bigger multi-part level */
1857
1858     name_first = level->name[0];
1859     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
1860
1861     is_multipart_level =
1862       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1863        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
1864
1865     is_first_part =
1866       ((name_first == '?' || name_first == '1') &&
1867        (name_last  == '?' || name_last  == '1'));
1868
1869     /* correct leading multipart level meta information in level name */
1870     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1871       level->name[i] = '-';
1872
1873     /* correct trailing multipart level meta information in level name */
1874     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1875       level->name[i] = '-';
1876
1877     /* ---------- check for normal single level ---------- */
1878
1879     if (!reading_multipart_level && !is_multipart_level)
1880     {
1881       /* the current level is simply a normal single-part level, and we are
1882          not reading a multi-part level yet, so return the level as it is */
1883
1884       break;
1885     }
1886
1887     /* ---------- check for empty level (unused multi-part) ---------- */
1888
1889     if (!reading_multipart_level && is_multipart_level && !is_first_part)
1890     {
1891       /* this is a part of a multi-part level, but not the first part
1892          (and we are not already reading parts of a multi-part level);
1893          in this case, use an empty level instead of the single part */
1894
1895       use_empty_level = TRUE;
1896
1897       break;
1898     }
1899
1900     /* ---------- check for finished multi-part level ---------- */
1901
1902     if (reading_multipart_level &&
1903         (!is_multipart_level ||
1904          strcmp(level->name, multipart_level.name) != 0))
1905     {
1906       /* we are already reading parts of a multi-part level, but this level is
1907          either not a multi-part level, or a part of a different multi-part
1908          level; in both cases, the multi-part level seems to be complete */
1909
1910       break;
1911     }
1912
1913     /* ---------- here we have one part of a multi-part level ---------- */
1914
1915     reading_multipart_level = TRUE;
1916
1917     if (is_first_part)  /* start with first part of new multi-part level */
1918     {
1919       /* copy level info structure from first part */
1920       multipart_level = *level;
1921
1922       /* clear playfield of new multi-part level */
1923       for (y = 0; y < MAX_LEV_FIELDY; y++)
1924         for (x = 0; x < MAX_LEV_FIELDX; x++)
1925           multipart_level.field[x][y] = EL_EMPTY;
1926     }
1927
1928     if (name_first == '?')
1929       name_first = '1';
1930     if (name_last == '?')
1931       name_last = '1';
1932
1933     multipart_xpos = (int)(name_first - '0');
1934     multipart_ypos = (int)(name_last  - '0');
1935
1936 #if 0
1937     printf("----------> part (%d/%d) of multi-part level '%s'\n",
1938            multipart_xpos, multipart_ypos, multipart_level.name);
1939 #endif
1940
1941     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1942         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1943     {
1944       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1945
1946       break;
1947     }
1948
1949     multipart_level.fieldx = MAX(multipart_level.fieldx,
1950                                  multipart_xpos * SP_LEVEL_XSIZE);
1951     multipart_level.fieldy = MAX(multipart_level.fieldy,
1952                                  multipart_ypos * SP_LEVEL_YSIZE);
1953
1954     /* copy level part at the right position of multi-part level */
1955     for (y = 0; y < SP_LEVEL_YSIZE; y++)
1956     {
1957       for (x = 0; x < SP_LEVEL_XSIZE; x++)
1958       {
1959         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1960         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1961
1962         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1963       }
1964     }
1965   }
1966
1967   fclose(file);
1968
1969   if (use_empty_level)
1970   {
1971     setLevelInfoToDefaults(level);
1972
1973     level->fieldx = SP_LEVEL_XSIZE;
1974     level->fieldy = SP_LEVEL_YSIZE;
1975
1976     for (y = 0; y < SP_LEVEL_YSIZE; y++)
1977       for (x = 0; x < SP_LEVEL_XSIZE; x++)
1978         level->field[x][y] = EL_EMPTY;
1979
1980     strcpy(level->name, "-------- EMPTY --------");
1981
1982     Error(ERR_WARN, "single part of multi-part level -- using empty level");
1983   }
1984
1985   if (reading_multipart_level)
1986     *level = multipart_level;
1987 }
1988
1989 /* ------------------------------------------------------------------------- */
1990 /* functions for loading generic level                                       */
1991 /* ------------------------------------------------------------------------- */
1992
1993 void LoadLevelFromFileInfo(struct LevelInfo *level,
1994                            struct LevelFileInfo *level_file_info)
1995 {
1996   /* always start with reliable default values */
1997   setLevelInfoToDefaults(level);
1998
1999   switch (level_file_info->type)
2000   {
2001     case LEVEL_FILE_TYPE_RND:
2002       LoadLevelFromFileInfo_RND(level, level_file_info);
2003       break;
2004
2005     case LEVEL_FILE_TYPE_EM:
2006       LoadLevelFromFileInfo_EM(level, level_file_info);
2007       break;
2008
2009     case LEVEL_FILE_TYPE_SP:
2010       LoadLevelFromFileInfo_SP(level, level_file_info);
2011       break;
2012
2013     default:
2014       LoadLevelFromFileInfo_RND(level, level_file_info);
2015       break;
2016   }
2017 }
2018
2019 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2020 {
2021   static struct LevelFileInfo level_file_info;
2022
2023   /* always start with reliable default values */
2024   setFileInfoToDefaults(&level_file_info);
2025
2026   level_file_info.nr = 0;                       /* unknown level number */
2027   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
2028   level_file_info.filename = filename;
2029
2030   LoadLevelFromFileInfo(level, &level_file_info);
2031 }
2032
2033 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2034 {
2035   if (leveldir_current == NULL)         /* only when dumping level */
2036     return;
2037
2038 #if 0
2039   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2040 #endif
2041
2042   /* determine correct game engine version of current level */
2043 #if 1
2044   if (!leveldir_current->latest_engine)
2045 #else
2046   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2047       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2048       IS_LEVELCLASS_UNDEFINED(leveldir_current))
2049 #endif
2050   {
2051 #if 0
2052     printf("\n::: This level is private or contributed: '%s'\n", filename);
2053 #endif
2054
2055 #if 0
2056     printf("\n::: Use the stored game engine version for this level\n");
2057 #endif
2058
2059     /* For all levels which are not forced to use the latest game engine
2060        version (normally user contributed, private and undefined levels),
2061        use the version of the game engine the levels were created for.
2062
2063        Since 2.0.1, the game engine version is now directly stored
2064        in the level file (chunk "VERS"), so there is no need anymore
2065        to set the game version from the file version (except for old,
2066        pre-2.0 levels, where the game version is still taken from the
2067        file format version used to store the level -- see above). */
2068
2069     /* do some special adjustments to support older level versions */
2070     if (level->file_version == FILE_VERSION_1_0)
2071     {
2072       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2073       Error(ERR_WARN, "using high speed movement for player");
2074
2075       /* player was faster than monsters in (pre-)1.0 levels */
2076       level->double_speed = TRUE;
2077     }
2078
2079     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
2080     if (level->game_version == VERSION_IDENT(2,0,1,0))
2081       level->em_slippery_gems = TRUE;
2082
2083     if (level->game_version < VERSION_IDENT(2,2,0,0))
2084       level->use_spring_bug = TRUE;
2085
2086     if (level->game_version < VERSION_IDENT(3,0,9,0))
2087     {
2088       int i, j;
2089
2090       level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2091
2092       setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
2093       setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2094       setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
2095       setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
2096
2097       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2098         SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2099
2100       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2101       {
2102         int element = EL_CUSTOM_START + i;
2103         struct ElementInfo *ei = &element_info[element];
2104
2105         for (j = 0; j < ei->num_change_pages; j++)
2106         {
2107           struct ElementChangeInfo *change = &ei->change_page[j];
2108
2109           change->trigger_player = CH_PLAYER_ANY;
2110           change->trigger_page = CH_PAGE_ANY;
2111         }
2112       }
2113     }
2114   }
2115   else
2116   {
2117 #if 0
2118     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2119            leveldir_current->sort_priority, filename);
2120 #endif
2121
2122 #if 0
2123     printf("\n::: Use latest game engine version for this level.\n");
2124 #endif
2125
2126     /* For all levels which are forced to use the latest game engine version
2127        (normally all but user contributed, private and undefined levels), set
2128        the game engine version to the actual version; this allows for actual
2129        corrections in the game engine to take effect for existing, converted
2130        levels (from "classic" or other existing games) to make the emulation
2131        of the corresponding game more accurate, while (hopefully) not breaking
2132        existing levels created from other players. */
2133
2134 #if 0
2135     printf("::: changing engine from %d to %d\n",
2136            level->game_version, GAME_VERSION_ACTUAL);
2137 #endif
2138
2139     level->game_version = GAME_VERSION_ACTUAL;
2140
2141     /* Set special EM style gems behaviour: EM style gems slip down from
2142        normal, steel and growing wall. As this is a more fundamental change,
2143        it seems better to set the default behaviour to "off" (as it is more
2144        natural) and make it configurable in the level editor (as a property
2145        of gem style elements). Already existing converted levels (neither
2146        private nor contributed levels) are changed to the new behaviour. */
2147
2148     if (level->file_version < FILE_VERSION_2_0)
2149       level->em_slippery_gems = TRUE;
2150   }
2151
2152 #if 0
2153   printf("::: => %d\n", level->game_version);
2154 #endif
2155 }
2156
2157 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2158 {
2159   int i, j, x, y;
2160
2161   /* map custom element change events that have changed in newer versions
2162      (these following values were accidentally changed in version 3.0.1) */
2163   if (level->game_version <= VERSION_IDENT(3,0,0,0))
2164   {
2165     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2166     {
2167       int element = EL_CUSTOM_START + i;
2168
2169       /* order of checking and copying events to be mapped is important */
2170       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2171       {
2172         if (HAS_CHANGE_EVENT(element, j - 2))
2173         {
2174           SET_CHANGE_EVENT(element, j - 2, FALSE);
2175           SET_CHANGE_EVENT(element, j, TRUE);
2176         }
2177       }
2178
2179       /* order of checking and copying events to be mapped is important */
2180       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2181       {
2182         if (HAS_CHANGE_EVENT(element, j - 1))
2183         {
2184           SET_CHANGE_EVENT(element, j - 1, FALSE);
2185           SET_CHANGE_EVENT(element, j, TRUE);
2186         }
2187       }
2188     }
2189   }
2190
2191   /* some custom element change events get mapped since version 3.0.3 */
2192   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2193   {
2194     int element = EL_CUSTOM_START + i;
2195
2196     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2197         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2198     {
2199       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2200       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2201
2202       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2203     }
2204   }
2205
2206   /* initialize "can_change" field for old levels with only one change page */
2207   if (level->game_version <= VERSION_IDENT(3,0,2,0))
2208   {
2209     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2210     {
2211       int element = EL_CUSTOM_START + i;
2212
2213       if (CAN_CHANGE(element))
2214         element_info[element].change->can_change = TRUE;
2215     }
2216   }
2217
2218   /* correct custom element values (for old levels without these options) */
2219   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2220   {
2221     int element = EL_CUSTOM_START + i;
2222     struct ElementInfo *ei = &element_info[element];
2223
2224     if (ei->access_direction == MV_NO_MOVING)
2225       ei->access_direction = MV_ALL_DIRECTIONS;
2226
2227     for (j = 0; j < ei->num_change_pages; j++)
2228     {
2229       struct ElementChangeInfo *change = &ei->change_page[j];
2230
2231       if (change->trigger_side == CH_SIDE_NONE)
2232         change->trigger_side = CH_SIDE_ANY;
2233     }
2234   }
2235
2236 #if 0
2237   /* set default push delay values (corrected since version 3.0.7-1) */
2238   if (level->game_version < VERSION_IDENT(3,0,7,1))
2239   {
2240     game.default_push_delay_fixed = 2;
2241     game.default_push_delay_random = 8;
2242   }
2243   else
2244   {
2245     game.default_push_delay_fixed = 8;
2246     game.default_push_delay_random = 8;
2247   }
2248
2249   /* set uninitialized push delay values of custom elements in older levels */
2250   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2251   {
2252     int element = EL_CUSTOM_START + i;
2253
2254     if (element_info[element].push_delay_fixed == -1)
2255       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2256     if (element_info[element].push_delay_random == -1)
2257       element_info[element].push_delay_random = game.default_push_delay_random;
2258   }
2259 #endif
2260
2261   /* map elements that have changed in newer versions */
2262   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2263                                                     level->game_version);
2264   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2265     for (x = 0; x < 3; x++)
2266       for (y = 0; y < 3; y++)
2267         level->yamyam_content[i][x][y] =
2268           getMappedElementByVersion(level->yamyam_content[i][x][y],
2269                                     level->game_version);
2270
2271   /* initialize element properties for level editor etc. */
2272   InitElementPropertiesEngine(level->game_version);
2273 }
2274
2275 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2276 {
2277   int x, y;
2278
2279   /* map elements that have changed in newer versions */
2280   for (y = 0; y < level->fieldy; y++)
2281   {
2282     for (x = 0; x < level->fieldx; x++)
2283     {
2284       int element = level->field[x][y];
2285
2286 #if 1
2287       element = getMappedElementByVersion(element, level->game_version);
2288 #else
2289       if (level->game_version <= VERSION_IDENT(2,2,0,0))
2290       {
2291         /* map game font elements */
2292         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2293                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2294                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2295                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2296       }
2297
2298       if (level->game_version < VERSION_IDENT(3,0,0,0))
2299       {
2300         /* map Supaplex gravity tube elements */
2301         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2302                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2303                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2304                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2305                    element);
2306       }
2307 #endif
2308
2309       level->field[x][y] = element;
2310     }
2311   }
2312
2313   /* copy elements to runtime playfield array */
2314   for (x = 0; x < MAX_LEV_FIELDX; x++)
2315     for (y = 0; y < MAX_LEV_FIELDY; y++)
2316       Feld[x][y] = level->field[x][y];
2317
2318   /* initialize level size variables for faster access */
2319   lev_fieldx = level->fieldx;
2320   lev_fieldy = level->fieldy;
2321
2322   /* determine border element for this level */
2323   SetBorderElement();
2324 }
2325
2326 void LoadLevelTemplate(int nr)
2327 {
2328 #if 1
2329   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2330   char *filename = level_file_info->filename;
2331
2332   LoadLevelFromFileInfo(&level_template, level_file_info);
2333 #else
2334   char *filename = getDefaultLevelFilename(nr);
2335
2336   LoadLevelFromFilename_RND(&level_template, filename);
2337 #endif
2338
2339   LoadLevel_InitVersion(&level, filename);
2340   LoadLevel_InitElements(&level, filename);
2341
2342   ActivateLevelTemplate();
2343 }
2344
2345 void LoadLevel(int nr)
2346 {
2347 #if 1
2348   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2349   char *filename = level_file_info->filename;
2350
2351   LoadLevelFromFileInfo(&level, level_file_info);
2352 #else
2353   char *filename = getLevelFilename(nr);
2354
2355   LoadLevelFromFilename_RND(&level, filename);
2356 #endif
2357
2358   if (level.use_custom_template)
2359     LoadLevelTemplate(-1);
2360
2361 #if 1
2362   LoadLevel_InitVersion(&level, filename);
2363   LoadLevel_InitElements(&level, filename);
2364   LoadLevel_InitPlayfield(&level, filename);
2365 #else
2366   LoadLevel_InitLevel(&level, filename);
2367 #endif
2368 }
2369
2370 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2371 {
2372   putFileVersion(file, level->file_version);
2373   putFileVersion(file, level->game_version);
2374 }
2375
2376 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2377 {
2378   int i, x, y;
2379
2380   putFile8Bit(file, level->fieldx);
2381   putFile8Bit(file, level->fieldy);
2382
2383   putFile16BitBE(file, level->time);
2384   putFile16BitBE(file, level->gems_needed);
2385
2386   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2387     putFile8Bit(file, level->name[i]);
2388
2389   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2390     putFile8Bit(file, level->score[i]);
2391
2392   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2393     for (y = 0; y < 3; y++)
2394       for (x = 0; x < 3; x++)
2395         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2396                            level->yamyam_content[i][x][y]));
2397   putFile8Bit(file, level->amoeba_speed);
2398   putFile8Bit(file, level->time_magic_wall);
2399   putFile8Bit(file, level->time_wheel);
2400   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2401                      level->amoeba_content));
2402   putFile8Bit(file, (level->double_speed ? 1 : 0));
2403   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2404   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2405   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2406
2407   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2408
2409   putFile8Bit(file, (level->block_last_field ? 1 : 0));
2410   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2411
2412   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2413
2414   putFile16BitBE(file, level->can_move_into_acid_bits);
2415
2416   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2417
2418   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2419 }
2420
2421 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2422 {
2423   int i;
2424
2425   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2426     putFile8Bit(file, level->author[i]);
2427 }
2428
2429 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2430 {
2431   int x, y;
2432
2433   for (y = 0; y < level->fieldy; y++) 
2434     for (x = 0; x < level->fieldx; x++) 
2435       if (level->encoding_16bit_field)
2436         putFile16BitBE(file, level->field[x][y]);
2437       else
2438         putFile8Bit(file, level->field[x][y]);
2439 }
2440
2441 #if 0
2442 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2443 {
2444   int i, x, y;
2445
2446   putFile8Bit(file, EL_YAMYAM);
2447   putFile8Bit(file, level->num_yamyam_contents);
2448   putFile8Bit(file, 0);
2449   putFile8Bit(file, 0);
2450
2451   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2452     for (y = 0; y < 3; y++)
2453       for (x = 0; x < 3; x++)
2454         if (level->encoding_16bit_field)
2455           putFile16BitBE(file, level->yamyam_content[i][x][y]);
2456         else
2457           putFile8Bit(file, level->yamyam_content[i][x][y]);
2458 }
2459 #endif
2460
2461 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2462 {
2463   int i, x, y;
2464   int num_contents, content_xsize, content_ysize;
2465   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2466
2467   if (element == EL_YAMYAM)
2468   {
2469     num_contents = level->num_yamyam_contents;
2470     content_xsize = 3;
2471     content_ysize = 3;
2472
2473     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2474       for (y = 0; y < 3; y++)
2475         for (x = 0; x < 3; x++)
2476           content_array[i][x][y] = level->yamyam_content[i][x][y];
2477   }
2478   else if (element == EL_BD_AMOEBA)
2479   {
2480     num_contents = 1;
2481     content_xsize = 1;
2482     content_ysize = 1;
2483
2484     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2485       for (y = 0; y < 3; y++)
2486         for (x = 0; x < 3; x++)
2487           content_array[i][x][y] = EL_EMPTY;
2488     content_array[0][0][0] = level->amoeba_content;
2489   }
2490   else
2491   {
2492     /* chunk header already written -- write empty chunk data */
2493     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2494
2495     Error(ERR_WARN, "cannot save content for element '%d'", element);
2496     return;
2497   }
2498
2499   putFile16BitBE(file, element);
2500   putFile8Bit(file, num_contents);
2501   putFile8Bit(file, content_xsize);
2502   putFile8Bit(file, content_ysize);
2503
2504   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2505
2506   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2507     for (y = 0; y < 3; y++)
2508       for (x = 0; x < 3; x++)
2509         putFile16BitBE(file, content_array[i][x][y]);
2510 }
2511
2512 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2513 {
2514   int i;
2515   int envelope_nr = element - EL_ENVELOPE_1;
2516   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2517
2518   putFile16BitBE(file, element);
2519   putFile16BitBE(file, envelope_len);
2520   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2521   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2522
2523   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2524
2525   for (i = 0; i < envelope_len; i++)
2526     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2527 }
2528
2529 #if 0
2530 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2531                            int num_changed_custom_elements)
2532 {
2533   int i, check = 0;
2534
2535   putFile16BitBE(file, num_changed_custom_elements);
2536
2537   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2538   {
2539     int element = EL_CUSTOM_START + i;
2540
2541     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2542     {
2543       if (check < num_changed_custom_elements)
2544       {
2545         putFile16BitBE(file, element);
2546         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2547       }
2548
2549       check++;
2550     }
2551   }
2552
2553   if (check != num_changed_custom_elements)     /* should not happen */
2554     Error(ERR_WARN, "inconsistent number of custom element properties");
2555 }
2556 #endif
2557
2558 #if 0
2559 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2560                            int num_changed_custom_elements)
2561 {
2562   int i, check = 0;
2563
2564   putFile16BitBE(file, num_changed_custom_elements);
2565
2566   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2567   {
2568     int element = EL_CUSTOM_START + i;
2569
2570     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2571     {
2572       if (check < num_changed_custom_elements)
2573       {
2574         putFile16BitBE(file, element);
2575         putFile16BitBE(file, element_info[element].change->target_element);
2576       }
2577
2578       check++;
2579     }
2580   }
2581
2582   if (check != num_changed_custom_elements)     /* should not happen */
2583     Error(ERR_WARN, "inconsistent number of custom target elements");
2584 }
2585 #endif
2586
2587 #if 0
2588 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2589                            int num_changed_custom_elements)
2590 {
2591   int i, j, x, y, check = 0;
2592
2593   putFile16BitBE(file, num_changed_custom_elements);
2594
2595   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2596   {
2597     int element = EL_CUSTOM_START + i;
2598
2599     if (element_info[element].modified_settings)
2600     {
2601       if (check < num_changed_custom_elements)
2602       {
2603         putFile16BitBE(file, element);
2604
2605         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2606           putFile8Bit(file, element_info[element].description[j]);
2607
2608         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2609
2610         /* some free bytes for future properties and padding */
2611         WriteUnusedBytesToFile(file, 7);
2612
2613         putFile8Bit(file, element_info[element].use_gfx_element);
2614         putFile16BitBE(file, element_info[element].gfx_element);
2615
2616         putFile8Bit(file, element_info[element].collect_score);
2617         putFile8Bit(file, element_info[element].collect_count);
2618
2619         putFile16BitBE(file, element_info[element].push_delay_fixed);
2620         putFile16BitBE(file, element_info[element].push_delay_random);
2621         putFile16BitBE(file, element_info[element].move_delay_fixed);
2622         putFile16BitBE(file, element_info[element].move_delay_random);
2623
2624         putFile16BitBE(file, element_info[element].move_pattern);
2625         putFile8Bit(file, element_info[element].move_direction_initial);
2626         putFile8Bit(file, element_info[element].move_stepsize);
2627
2628         for (y = 0; y < 3; y++)
2629           for (x = 0; x < 3; x++)
2630             putFile16BitBE(file, element_info[element].content[x][y]);
2631
2632         putFile32BitBE(file, element_info[element].change->events);
2633
2634         putFile16BitBE(file, element_info[element].change->target_element);
2635
2636         putFile16BitBE(file, element_info[element].change->delay_fixed);
2637         putFile16BitBE(file, element_info[element].change->delay_random);
2638         putFile16BitBE(file, element_info[element].change->delay_frames);
2639
2640         putFile16BitBE(file, element_info[element].change->trigger_element);
2641
2642         putFile8Bit(file, element_info[element].change->explode);
2643         putFile8Bit(file, element_info[element].change->use_content);
2644         putFile8Bit(file, element_info[element].change->only_complete);
2645         putFile8Bit(file, element_info[element].change->use_random_change);
2646
2647         putFile8Bit(file, element_info[element].change->random);
2648         putFile8Bit(file, element_info[element].change->power);
2649
2650         for (y = 0; y < 3; y++)
2651           for (x = 0; x < 3; x++)
2652             putFile16BitBE(file, element_info[element].change->content[x][y]);
2653
2654         putFile8Bit(file, element_info[element].slippery_type);
2655
2656         /* some free bytes for future properties and padding */
2657         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2658       }
2659
2660       check++;
2661     }
2662   }
2663
2664   if (check != num_changed_custom_elements)     /* should not happen */
2665     Error(ERR_WARN, "inconsistent number of custom element properties");
2666 }
2667 #endif
2668
2669 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2670 {
2671   struct ElementInfo *ei = &element_info[element];
2672   int i, x, y;
2673
2674   putFile16BitBE(file, element);
2675
2676   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2677     putFile8Bit(file, ei->description[i]);
2678
2679   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2680   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
2681
2682   putFile8Bit(file, ei->num_change_pages);
2683
2684   /* some free bytes for future base property values and padding */
2685   WriteUnusedBytesToFile(file, 5);
2686
2687   /* write custom property values */
2688
2689   putFile8Bit(file, ei->use_gfx_element);
2690   putFile16BitBE(file, ei->gfx_element);
2691
2692   putFile8Bit(file, ei->collect_score);
2693   putFile8Bit(file, ei->collect_count);
2694
2695   putFile16BitBE(file, ei->push_delay_fixed);
2696   putFile16BitBE(file, ei->push_delay_random);
2697   putFile16BitBE(file, ei->move_delay_fixed);
2698   putFile16BitBE(file, ei->move_delay_random);
2699
2700   /* bits 0 - 15 of "move_pattern" ... */
2701   putFile16BitBE(file, ei->move_pattern & 0xffff);
2702   putFile8Bit(file, ei->move_direction_initial);
2703   putFile8Bit(file, ei->move_stepsize);
2704
2705   putFile8Bit(file, ei->slippery_type);
2706
2707   for (y = 0; y < 3; y++)
2708     for (x = 0; x < 3; x++)
2709       putFile16BitBE(file, ei->content[x][y]);
2710
2711   putFile16BitBE(file, ei->move_enter_element);
2712   putFile16BitBE(file, ei->move_leave_element);
2713   putFile8Bit(file, ei->move_leave_type);
2714
2715   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2716   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2717
2718   putFile8Bit(file, ei->access_direction);
2719
2720   putFile8Bit(file, ei->explosion_delay);
2721   putFile8Bit(file, ei->ignition_delay);
2722
2723   /* some free bytes for future custom property values and padding */
2724   WriteUnusedBytesToFile(file, 2);
2725
2726   /* write change property values */
2727
2728   for (i = 0; i < ei->num_change_pages; i++)
2729   {
2730     struct ElementChangeInfo *change = &ei->change_page[i];
2731
2732     putFile32BitBE(file, change->events);
2733
2734     putFile16BitBE(file, change->target_element);
2735
2736     putFile16BitBE(file, change->delay_fixed);
2737     putFile16BitBE(file, change->delay_random);
2738     putFile16BitBE(file, change->delay_frames);
2739
2740     putFile16BitBE(file, change->trigger_element);
2741
2742     putFile8Bit(file, change->explode);
2743     putFile8Bit(file, change->use_content);
2744     putFile8Bit(file, change->only_complete);
2745     putFile8Bit(file, change->use_random_change);
2746
2747     putFile8Bit(file, change->random);
2748     putFile8Bit(file, change->power);
2749
2750     for (y = 0; y < 3; y++)
2751       for (x = 0; x < 3; x++)
2752         putFile16BitBE(file, change->content[x][y]);
2753
2754     putFile8Bit(file, change->can_change);
2755
2756     putFile8Bit(file, change->trigger_side);
2757
2758 #if 1
2759     putFile8Bit(file, change->trigger_player);
2760     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
2761                        log_2(change->trigger_page)));
2762
2763     /* some free bytes for future change property values and padding */
2764     WriteUnusedBytesToFile(file, 6);
2765
2766 #else
2767
2768     /* some free bytes for future change property values and padding */
2769     WriteUnusedBytesToFile(file, 8);
2770 #endif
2771   }
2772 }
2773
2774 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2775 {
2776   struct ElementInfo *ei = &element_info[element];
2777   struct ElementGroupInfo *group = ei->group;
2778   int i;
2779
2780   putFile16BitBE(file, element);
2781
2782   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2783     putFile8Bit(file, ei->description[i]);
2784
2785   putFile8Bit(file, group->num_elements);
2786
2787   putFile8Bit(file, ei->use_gfx_element);
2788   putFile16BitBE(file, ei->gfx_element);
2789
2790   putFile8Bit(file, group->choice_mode);
2791
2792   /* some free bytes for future values and padding */
2793   WriteUnusedBytesToFile(file, 3);
2794
2795   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2796     putFile16BitBE(file, group->element[i]);
2797 }
2798
2799 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2800 {
2801   int body_chunk_size;
2802   int i, x, y;
2803   FILE *file;
2804
2805   if (!(file = fopen(filename, MODE_WRITE)))
2806   {
2807     Error(ERR_WARN, "cannot save level file '%s'", filename);
2808     return;
2809   }
2810
2811   level->file_version = FILE_VERSION_ACTUAL;
2812   level->game_version = GAME_VERSION_ACTUAL;
2813
2814   /* check level field for 16-bit elements */
2815   level->encoding_16bit_field = FALSE;
2816   for (y = 0; y < level->fieldy; y++) 
2817     for (x = 0; x < level->fieldx; x++) 
2818       if (level->field[x][y] > 255)
2819         level->encoding_16bit_field = TRUE;
2820
2821   /* check yamyam content for 16-bit elements */
2822   level->encoding_16bit_yamyam = FALSE;
2823   for (i = 0; i < level->num_yamyam_contents; i++)
2824     for (y = 0; y < 3; y++)
2825       for (x = 0; x < 3; x++)
2826         if (level->yamyam_content[i][x][y] > 255)
2827           level->encoding_16bit_yamyam = TRUE;
2828
2829   /* check amoeba content for 16-bit elements */
2830   level->encoding_16bit_amoeba = FALSE;
2831   if (level->amoeba_content > 255)
2832     level->encoding_16bit_amoeba = TRUE;
2833
2834   /* calculate size of "BODY" chunk */
2835   body_chunk_size =
2836     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2837
2838   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2839   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2840
2841   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2842   SaveLevel_VERS(file, level);
2843
2844   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2845   SaveLevel_HEAD(file, level);
2846
2847   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2848   SaveLevel_AUTH(file, level);
2849
2850   putFileChunkBE(file, "BODY", body_chunk_size);
2851   SaveLevel_BODY(file, level);
2852
2853   if (level->encoding_16bit_yamyam ||
2854       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2855   {
2856     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2857     SaveLevel_CNT2(file, level, EL_YAMYAM);
2858   }
2859
2860   if (level->encoding_16bit_amoeba)
2861   {
2862     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2863     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2864   }
2865
2866   /* check for envelope content */
2867   for (i = 0; i < 4; i++)
2868   {
2869     if (strlen(level->envelope_text[i]) > 0)
2870     {
2871       int envelope_len = strlen(level->envelope_text[i]) + 1;
2872
2873       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2874       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2875     }
2876   }
2877
2878   /* check for non-default custom elements (unless using template level) */
2879   if (!level->use_custom_template)
2880   {
2881     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2882     {
2883       int element = EL_CUSTOM_START + i;
2884
2885       if (element_info[element].modified_settings)
2886       {
2887         int num_change_pages = element_info[element].num_change_pages;
2888
2889         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2890         SaveLevel_CUS4(file, level, element);
2891       }
2892     }
2893   }
2894
2895   /* check for non-default group elements (unless using template level) */
2896   if (!level->use_custom_template)
2897   {
2898     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2899     {
2900       int element = EL_GROUP_START + i;
2901
2902       if (element_info[element].modified_settings)
2903       {
2904         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2905         SaveLevel_GRP1(file, level, element);
2906       }
2907     }
2908   }
2909
2910   fclose(file);
2911
2912   SetFilePermissions(filename, PERMS_PRIVATE);
2913 }
2914
2915 void SaveLevel(int nr)
2916 {
2917   char *filename = getDefaultLevelFilename(nr);
2918
2919   SaveLevelFromFilename(&level, filename);
2920 }
2921
2922 void SaveLevelTemplate()
2923 {
2924   char *filename = getDefaultLevelFilename(-1);
2925
2926   SaveLevelFromFilename(&level, filename);
2927 }
2928
2929 void DumpLevel(struct LevelInfo *level)
2930 {
2931   printf_line("-", 79);
2932   printf("Level xxx (file version %08d, game version %08d)\n",
2933          level->file_version, level->game_version);
2934   printf_line("-", 79);
2935
2936   printf("Level author: '%s'\n", level->author);
2937   printf("Level title:  '%s'\n", level->name);
2938   printf("\n");
2939   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2940   printf("\n");
2941   printf("Level time:  %d seconds\n", level->time);
2942   printf("Gems needed: %d\n", level->gems_needed);
2943   printf("\n");
2944   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2945   printf("Time for wheel:      %d seconds\n", level->time_wheel);
2946   printf("Time for light:      %d seconds\n", level->time_light);
2947   printf("Time for timegate:   %d seconds\n", level->time_timegate);
2948   printf("\n");
2949   printf("Amoeba speed: %d\n", level->amoeba_speed);
2950   printf("\n");
2951   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
2952   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
2953   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
2954   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
2955   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2956   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2957   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
2958
2959   printf_line("-", 79);
2960 }
2961
2962
2963 /* ========================================================================= */
2964 /* tape file functions                                                       */
2965 /* ========================================================================= */
2966
2967 static void setTapeInfoToDefaults()
2968 {
2969   int i;
2970
2971   /* always start with reliable default values (empty tape) */
2972   TapeErase();
2973
2974   /* default values (also for pre-1.2 tapes) with only the first player */
2975   tape.player_participates[0] = TRUE;
2976   for (i = 1; i < MAX_PLAYERS; i++)
2977     tape.player_participates[i] = FALSE;
2978
2979   /* at least one (default: the first) player participates in every tape */
2980   tape.num_participating_players = 1;
2981
2982   tape.level_nr = level_nr;
2983   tape.counter = 0;
2984   tape.changed = FALSE;
2985
2986   tape.recording = FALSE;
2987   tape.playing = FALSE;
2988   tape.pausing = FALSE;
2989 }
2990
2991 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2992 {
2993   tape->file_version = getFileVersion(file);
2994   tape->game_version = getFileVersion(file);
2995
2996   return chunk_size;
2997 }
2998
2999 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3000 {
3001   int i;
3002
3003   tape->random_seed = getFile32BitBE(file);
3004   tape->date        = getFile32BitBE(file);
3005   tape->length      = getFile32BitBE(file);
3006
3007   /* read header fields that are new since version 1.2 */
3008   if (tape->file_version >= FILE_VERSION_1_2)
3009   {
3010     byte store_participating_players = getFile8Bit(file);
3011     int engine_version;
3012
3013     /* since version 1.2, tapes store which players participate in the tape */
3014     tape->num_participating_players = 0;
3015     for (i = 0; i < MAX_PLAYERS; i++)
3016     {
3017       tape->player_participates[i] = FALSE;
3018
3019       if (store_participating_players & (1 << i))
3020       {
3021         tape->player_participates[i] = TRUE;
3022         tape->num_participating_players++;
3023       }
3024     }
3025
3026     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3027
3028     engine_version = getFileVersion(file);
3029     if (engine_version > 0)
3030       tape->engine_version = engine_version;
3031     else
3032       tape->engine_version = tape->game_version;
3033   }
3034
3035   return chunk_size;
3036 }
3037
3038 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3039 {
3040   int level_identifier_size;
3041   int i;
3042
3043   level_identifier_size = getFile16BitBE(file);
3044
3045   tape->level_identifier =
3046     checked_realloc(tape->level_identifier, level_identifier_size);
3047
3048   for (i = 0; i < level_identifier_size; i++)
3049     tape->level_identifier[i] = getFile8Bit(file);
3050
3051   tape->level_nr = getFile16BitBE(file);
3052
3053   chunk_size = 2 + level_identifier_size + 2;
3054
3055   return chunk_size;
3056 }
3057
3058 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3059 {
3060   int i, j;
3061   int chunk_size_expected =
3062     (tape->num_participating_players + 1) * tape->length;
3063
3064   if (chunk_size_expected != chunk_size)
3065   {
3066     ReadUnusedBytesFromFile(file, chunk_size);
3067     return chunk_size_expected;
3068   }
3069
3070   for (i = 0; i < tape->length; i++)
3071   {
3072     if (i >= MAX_TAPELEN)
3073       break;
3074
3075     for (j = 0; j < MAX_PLAYERS; j++)
3076     {
3077       tape->pos[i].action[j] = MV_NO_MOVING;
3078
3079       if (tape->player_participates[j])
3080         tape->pos[i].action[j] = getFile8Bit(file);
3081     }
3082
3083     tape->pos[i].delay = getFile8Bit(file);
3084
3085     if (tape->file_version == FILE_VERSION_1_0)
3086     {
3087       /* eliminate possible diagonal moves in old tapes */
3088       /* this is only for backward compatibility */
3089
3090       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3091       byte action = tape->pos[i].action[0];
3092       int k, num_moves = 0;
3093
3094       for (k = 0; k<4; k++)
3095       {
3096         if (action & joy_dir[k])
3097         {
3098           tape->pos[i + num_moves].action[0] = joy_dir[k];
3099           if (num_moves > 0)
3100             tape->pos[i + num_moves].delay = 0;
3101           num_moves++;
3102         }
3103       }
3104
3105       if (num_moves > 1)
3106       {
3107         num_moves--;
3108         i += num_moves;
3109         tape->length += num_moves;
3110       }
3111     }
3112     else if (tape->file_version < FILE_VERSION_2_0)
3113     {
3114       /* convert pre-2.0 tapes to new tape format */
3115
3116       if (tape->pos[i].delay > 1)
3117       {
3118         /* action part */
3119         tape->pos[i + 1] = tape->pos[i];
3120         tape->pos[i + 1].delay = 1;
3121
3122         /* delay part */
3123         for (j = 0; j < MAX_PLAYERS; j++)
3124           tape->pos[i].action[j] = MV_NO_MOVING;
3125         tape->pos[i].delay--;
3126
3127         i++;
3128         tape->length++;
3129       }
3130     }
3131
3132     if (feof(file))
3133       break;
3134   }
3135
3136   if (i != tape->length)
3137     chunk_size = (tape->num_participating_players + 1) * i;
3138
3139   return chunk_size;
3140 }
3141
3142 void LoadTapeFromFilename(char *filename)
3143 {
3144   char cookie[MAX_LINE_LEN];
3145   char chunk_name[CHUNK_ID_LEN + 1];
3146   FILE *file;
3147   int chunk_size;
3148
3149   /* always start with reliable default values */
3150   setTapeInfoToDefaults();
3151
3152   if (!(file = fopen(filename, MODE_READ)))
3153     return;
3154
3155   getFileChunkBE(file, chunk_name, NULL);
3156   if (strcmp(chunk_name, "RND1") == 0)
3157   {
3158     getFile32BitBE(file);               /* not used */
3159
3160     getFileChunkBE(file, chunk_name, NULL);
3161     if (strcmp(chunk_name, "TAPE") != 0)
3162     {
3163       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3164       fclose(file);
3165       return;
3166     }
3167   }
3168   else  /* check for pre-2.0 file format with cookie string */
3169   {
3170     strcpy(cookie, chunk_name);
3171     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3172     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3173       cookie[strlen(cookie) - 1] = '\0';
3174
3175     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3176     {
3177       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3178       fclose(file);
3179       return;
3180     }
3181
3182     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3183     {
3184       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3185       fclose(file);
3186       return;
3187     }
3188
3189     /* pre-2.0 tape files have no game version, so use file version here */
3190     tape.game_version = tape.file_version;
3191   }
3192
3193   if (tape.file_version < FILE_VERSION_1_2)
3194   {
3195     /* tape files from versions before 1.2.0 without chunk structure */
3196     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3197     LoadTape_BODY(file, 2 * tape.length,  &tape);
3198   }
3199   else
3200   {
3201     static struct
3202     {
3203       char *name;
3204       int size;
3205       int (*loader)(FILE *, int, struct TapeInfo *);
3206     }
3207     chunk_info[] =
3208     {
3209       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
3210       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
3211       { "INFO", -1,                     LoadTape_INFO },
3212       { "BODY", -1,                     LoadTape_BODY },
3213       {  NULL,  0,                      NULL }
3214     };
3215
3216     while (getFileChunkBE(file, chunk_name, &chunk_size))
3217     {
3218       int i = 0;
3219
3220       while (chunk_info[i].name != NULL &&
3221              strcmp(chunk_name, chunk_info[i].name) != 0)
3222         i++;
3223
3224       if (chunk_info[i].name == NULL)
3225       {
3226         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3227               chunk_name, filename);
3228         ReadUnusedBytesFromFile(file, chunk_size);
3229       }
3230       else if (chunk_info[i].size != -1 &&
3231                chunk_info[i].size != chunk_size)
3232       {
3233         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3234               chunk_size, chunk_name, filename);
3235         ReadUnusedBytesFromFile(file, chunk_size);
3236       }
3237       else
3238       {
3239         /* call function to load this tape chunk */
3240         int chunk_size_expected =
3241           (chunk_info[i].loader)(file, chunk_size, &tape);
3242
3243         /* the size of some chunks cannot be checked before reading other
3244            chunks first (like "HEAD" and "BODY") that contain some header
3245            information, so check them here */
3246         if (chunk_size_expected != chunk_size)
3247         {
3248           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3249                 chunk_size, chunk_name, filename);
3250         }
3251       }
3252     }
3253   }
3254
3255   fclose(file);
3256
3257   tape.length_seconds = GetTapeLength();
3258
3259 #if 0
3260   printf("::: tape game version: %d\n", tape.game_version);
3261   printf("::: tape engine version: %d\n", tape.engine_version);
3262 #endif
3263 }
3264
3265 void LoadTape(int nr)
3266 {
3267   char *filename = getTapeFilename(nr);
3268
3269   LoadTapeFromFilename(filename);
3270 }
3271
3272 void LoadSolutionTape(int nr)
3273 {
3274   char *filename = getSolutionTapeFilename(nr);
3275
3276   LoadTapeFromFilename(filename);
3277 }
3278
3279 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3280 {
3281   putFileVersion(file, tape->file_version);
3282   putFileVersion(file, tape->game_version);
3283 }
3284
3285 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3286 {
3287   int i;
3288   byte store_participating_players = 0;
3289
3290   /* set bits for participating players for compact storage */
3291   for (i = 0; i < MAX_PLAYERS; i++)
3292     if (tape->player_participates[i])
3293       store_participating_players |= (1 << i);
3294
3295   putFile32BitBE(file, tape->random_seed);
3296   putFile32BitBE(file, tape->date);
3297   putFile32BitBE(file, tape->length);
3298
3299   putFile8Bit(file, store_participating_players);
3300
3301   /* unused bytes not at the end here for 4-byte alignment of engine_version */
3302   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3303
3304   putFileVersion(file, tape->engine_version);
3305 }
3306
3307 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3308 {
3309   int level_identifier_size = strlen(tape->level_identifier) + 1;
3310   int i;
3311
3312   putFile16BitBE(file, level_identifier_size);
3313
3314   for (i = 0; i < level_identifier_size; i++)
3315     putFile8Bit(file, tape->level_identifier[i]);
3316
3317   putFile16BitBE(file, tape->level_nr);
3318 }
3319
3320 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3321 {
3322   int i, j;
3323
3324   for (i = 0; i < tape->length; i++)
3325   {
3326     for (j = 0; j < MAX_PLAYERS; j++)
3327       if (tape->player_participates[j])
3328         putFile8Bit(file, tape->pos[i].action[j]);
3329
3330     putFile8Bit(file, tape->pos[i].delay);
3331   }
3332 }
3333
3334 void SaveTape(int nr)
3335 {
3336   char *filename = getTapeFilename(nr);
3337   FILE *file;
3338   boolean new_tape = TRUE;
3339   int num_participating_players = 0;
3340   int info_chunk_size;
3341   int body_chunk_size;
3342   int i;
3343
3344   InitTapeDirectory(leveldir_current->subdir);
3345
3346   /* if a tape still exists, ask to overwrite it */
3347   if (access(filename, F_OK) == 0)
3348   {
3349     new_tape = FALSE;
3350     if (!Request("Replace old tape ?", REQ_ASK))
3351       return;
3352   }
3353
3354   if (!(file = fopen(filename, MODE_WRITE)))
3355   {
3356     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3357     return;
3358   }
3359
3360   tape.file_version = FILE_VERSION_ACTUAL;
3361   tape.game_version = GAME_VERSION_ACTUAL;
3362
3363   /* count number of participating players  */
3364   for (i = 0; i < MAX_PLAYERS; i++)
3365     if (tape.player_participates[i])
3366       num_participating_players++;
3367
3368   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3369   body_chunk_size = (num_participating_players + 1) * tape.length;
3370
3371   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3372   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3373
3374   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3375   SaveTape_VERS(file, &tape);
3376
3377   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3378   SaveTape_HEAD(file, &tape);
3379
3380   putFileChunkBE(file, "INFO", info_chunk_size);
3381   SaveTape_INFO(file, &tape);
3382
3383   putFileChunkBE(file, "BODY", body_chunk_size);
3384   SaveTape_BODY(file, &tape);
3385
3386   fclose(file);
3387
3388   SetFilePermissions(filename, PERMS_PRIVATE);
3389
3390   tape.changed = FALSE;
3391
3392   if (new_tape)
3393     Request("tape saved !", REQ_CONFIRM);
3394 }
3395
3396 void DumpTape(struct TapeInfo *tape)
3397 {
3398   int i, j;
3399
3400   if (TAPE_IS_EMPTY(*tape))
3401   {
3402     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3403     return;
3404   }
3405
3406   printf_line("-", 79);
3407   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3408          tape->level_nr, tape->file_version, tape->game_version);
3409   printf("Level series identifier: '%s'\n", tape->level_identifier);
3410   printf_line("-", 79);
3411
3412   for (i = 0; i < tape->length; i++)
3413   {
3414     if (i >= MAX_TAPELEN)
3415       break;
3416
3417     printf("%03d: ", i);
3418
3419     for (j = 0; j < MAX_PLAYERS; j++)
3420     {
3421       if (tape->player_participates[j])
3422       {
3423         int action = tape->pos[i].action[j];
3424
3425         printf("%d:%02x ", j, action);
3426         printf("[%c%c%c%c|%c%c] - ",
3427                (action & JOY_LEFT ? '<' : ' '),
3428                (action & JOY_RIGHT ? '>' : ' '),
3429                (action & JOY_UP ? '^' : ' '),
3430                (action & JOY_DOWN ? 'v' : ' '),
3431                (action & JOY_BUTTON_1 ? '1' : ' '),
3432                (action & JOY_BUTTON_2 ? '2' : ' '));
3433       }
3434     }
3435
3436     printf("(%03d)\n", tape->pos[i].delay);
3437   }
3438
3439   printf_line("-", 79);
3440 }
3441
3442
3443 /* ========================================================================= */
3444 /* score file functions                                                      */
3445 /* ========================================================================= */
3446
3447 void LoadScore(int nr)
3448 {
3449   int i;
3450   char *filename = getScoreFilename(nr);
3451   char cookie[MAX_LINE_LEN];
3452   char line[MAX_LINE_LEN];
3453   char *line_ptr;
3454   FILE *file;
3455
3456   /* always start with reliable default values */
3457   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3458   {
3459     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3460     highscore[i].Score = 0;
3461   }
3462
3463   if (!(file = fopen(filename, MODE_READ)))
3464     return;
3465
3466   /* check file identifier */
3467   fgets(cookie, MAX_LINE_LEN, file);
3468   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3469     cookie[strlen(cookie) - 1] = '\0';
3470
3471   if (!checkCookieString(cookie, SCORE_COOKIE))
3472   {
3473     Error(ERR_WARN, "unknown format of score file '%s'", filename);
3474     fclose(file);
3475     return;
3476   }
3477
3478   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3479   {
3480     fscanf(file, "%d", &highscore[i].Score);
3481     fgets(line, MAX_LINE_LEN, file);
3482
3483     if (line[strlen(line) - 1] == '\n')
3484       line[strlen(line) - 1] = '\0';
3485
3486     for (line_ptr = line; *line_ptr; line_ptr++)
3487     {
3488       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3489       {
3490         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3491         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3492         break;
3493       }
3494     }
3495   }
3496
3497   fclose(file);
3498 }
3499
3500 void SaveScore(int nr)
3501 {
3502   int i;
3503   char *filename = getScoreFilename(nr);
3504   FILE *file;
3505
3506   InitScoreDirectory(leveldir_current->subdir);
3507
3508   if (!(file = fopen(filename, MODE_WRITE)))
3509   {
3510     Error(ERR_WARN, "cannot save score for level %d", nr);
3511     return;
3512   }
3513
3514   fprintf(file, "%s\n\n", SCORE_COOKIE);
3515
3516   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3517     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3518
3519   fclose(file);
3520
3521   SetFilePermissions(filename, PERMS_PUBLIC);
3522 }
3523
3524
3525 /* ========================================================================= */
3526 /* setup file functions                                                      */
3527 /* ========================================================================= */
3528
3529 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
3530
3531 /* global setup */
3532 #define SETUP_TOKEN_PLAYER_NAME                 0
3533 #define SETUP_TOKEN_SOUND                       1
3534 #define SETUP_TOKEN_SOUND_LOOPS                 2
3535 #define SETUP_TOKEN_SOUND_MUSIC                 3
3536 #define SETUP_TOKEN_SOUND_SIMPLE                4
3537 #define SETUP_TOKEN_TOONS                       5
3538 #define SETUP_TOKEN_SCROLL_DELAY                6
3539 #define SETUP_TOKEN_SOFT_SCROLLING              7
3540 #define SETUP_TOKEN_FADING                      8
3541 #define SETUP_TOKEN_AUTORECORD                  9
3542 #define SETUP_TOKEN_QUICK_DOORS                 10
3543 #define SETUP_TOKEN_TEAM_MODE                   11
3544 #define SETUP_TOKEN_HANDICAP                    12
3545 #define SETUP_TOKEN_TIME_LIMIT                  13
3546 #define SETUP_TOKEN_FULLSCREEN                  14
3547 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
3548 #define SETUP_TOKEN_GRAPHICS_SET                16
3549 #define SETUP_TOKEN_SOUNDS_SET                  17
3550 #define SETUP_TOKEN_MUSIC_SET                   18
3551 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
3552 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
3553 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
3554
3555 #define NUM_GLOBAL_SETUP_TOKENS                 22
3556
3557 /* editor setup */
3558 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
3559 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
3560 #define SETUP_TOKEN_EDITOR_EL_MORE              2
3561 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
3562 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
3563 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
3564 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
3565 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
3566 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
3567 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
3568 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
3569 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
3570
3571 #define NUM_EDITOR_SETUP_TOKENS                 12
3572
3573 /* shortcut setup */
3574 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
3575 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
3576 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
3577
3578 #define NUM_SHORTCUT_SETUP_TOKENS               3
3579
3580 /* player setup */
3581 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
3582 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
3583 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
3584 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
3585 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
3586 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
3587 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
3588 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
3589 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
3590 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
3591 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
3592 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
3593 #define SETUP_TOKEN_PLAYER_KEY_UP               12
3594 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
3595 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
3596 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
3597
3598 #define NUM_PLAYER_SETUP_TOKENS                 16
3599
3600 /* system setup */
3601 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
3602 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
3603
3604 #define NUM_SYSTEM_SETUP_TOKENS                 2
3605
3606 /* options setup */
3607 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
3608
3609 #define NUM_OPTIONS_SETUP_TOKENS                1
3610
3611
3612 static struct SetupInfo si;
3613 static struct SetupEditorInfo sei;
3614 static struct SetupShortcutInfo ssi;
3615 static struct SetupInputInfo sii;
3616 static struct SetupSystemInfo syi;
3617 static struct OptionInfo soi;
3618
3619 static struct TokenInfo global_setup_tokens[] =
3620 {
3621   { TYPE_STRING, &si.player_name,       "player_name"                   },
3622   { TYPE_SWITCH, &si.sound,             "sound"                         },
3623   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
3624   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
3625   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
3626   { TYPE_SWITCH, &si.toons,             "toons"                         },
3627   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
3628   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
3629   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
3630   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
3631   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
3632   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
3633   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
3634   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
3635   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
3636   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
3637   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
3638   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
3639   { TYPE_STRING, &si.music_set,         "music_set"                     },
3640   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3641   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
3642   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
3643 };
3644
3645 static struct TokenInfo editor_setup_tokens[] =
3646 {
3647   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
3648   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
3649   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
3650   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
3651   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
3652   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
3653   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
3654   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
3655   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
3656   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
3657   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
3658   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
3659 };
3660
3661 static struct TokenInfo shortcut_setup_tokens[] =
3662 {
3663   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
3664   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
3665   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
3666 };
3667
3668 static struct TokenInfo player_setup_tokens[] =
3669 {
3670   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
3671   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
3672   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
3673   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
3674   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
3675   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
3676   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
3677   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
3678   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
3679   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
3680   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
3681   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
3682   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
3683   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
3684   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
3685   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
3686 };
3687
3688 static struct TokenInfo system_setup_tokens[] =
3689 {
3690   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
3691   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3692 };
3693
3694 static struct TokenInfo options_setup_tokens[] =
3695 {
3696   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
3697 };
3698
3699 static char *get_corrected_login_name(char *login_name)
3700 {
3701   /* needed because player name must be a fixed length string */
3702   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3703
3704   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3705   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3706
3707   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
3708     if (strchr(login_name_new, ' '))
3709       *strchr(login_name_new, ' ') = '\0';
3710
3711   return login_name_new;
3712 }
3713
3714 static void setSetupInfoToDefaults(struct SetupInfo *si)
3715 {
3716   int i;
3717
3718   si->player_name = get_corrected_login_name(getLoginName());
3719
3720   si->sound = TRUE;
3721   si->sound_loops = TRUE;
3722   si->sound_music = TRUE;
3723   si->sound_simple = TRUE;
3724   si->toons = TRUE;
3725   si->double_buffering = TRUE;
3726   si->direct_draw = !si->double_buffering;
3727   si->scroll_delay = TRUE;
3728   si->soft_scrolling = TRUE;
3729   si->fading = FALSE;
3730   si->autorecord = TRUE;
3731   si->quick_doors = FALSE;
3732   si->team_mode = FALSE;
3733   si->handicap = TRUE;
3734   si->time_limit = TRUE;
3735   si->fullscreen = FALSE;
3736   si->ask_on_escape = TRUE;
3737
3738   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3739   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3740   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3741   si->override_level_graphics = FALSE;
3742   si->override_level_sounds = FALSE;
3743   si->override_level_music = FALSE;
3744
3745   si->editor.el_boulderdash = TRUE;
3746   si->editor.el_emerald_mine = TRUE;
3747   si->editor.el_more = TRUE;
3748   si->editor.el_sokoban = TRUE;
3749   si->editor.el_supaplex = TRUE;
3750   si->editor.el_diamond_caves = TRUE;
3751   si->editor.el_dx_boulderdash = TRUE;
3752   si->editor.el_chars = TRUE;
3753   si->editor.el_custom = TRUE;
3754   si->editor.el_custom_more = FALSE;
3755
3756   si->editor.el_headlines = TRUE;
3757   si->editor.el_user_defined = FALSE;
3758
3759   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3760   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3761   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3762
3763   for (i = 0; i < MAX_PLAYERS; i++)
3764   {
3765     si->input[i].use_joystick = FALSE;
3766     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3767     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
3768     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3769     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
3770     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
3771     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3772     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
3773     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
3774     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
3775     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
3776     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3777     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
3778     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
3779     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
3780     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
3781   }
3782
3783   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3784   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3785
3786   si->options.verbose = FALSE;
3787 }
3788
3789 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3790 {
3791   int i, pnr;
3792
3793   if (!setup_file_hash)
3794     return;
3795
3796   /* global setup */
3797   si = setup;
3798   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3799     setSetupInfo(global_setup_tokens, i,
3800                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3801   setup = si;
3802
3803   /* editor setup */
3804   sei = setup.editor;
3805   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3806     setSetupInfo(editor_setup_tokens, i,
3807                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3808   setup.editor = sei;
3809
3810   /* shortcut setup */
3811   ssi = setup.shortcut;
3812   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3813     setSetupInfo(shortcut_setup_tokens, i,
3814                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3815   setup.shortcut = ssi;
3816
3817   /* player setup */
3818   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3819   {
3820     char prefix[30];
3821
3822     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3823
3824     sii = setup.input[pnr];
3825     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3826     {
3827       char full_token[100];
3828
3829       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3830       setSetupInfo(player_setup_tokens, i,
3831                    getHashEntry(setup_file_hash, full_token));
3832     }
3833     setup.input[pnr] = sii;
3834   }
3835
3836   /* system setup */
3837   syi = setup.system;
3838   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3839     setSetupInfo(system_setup_tokens, i,
3840                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3841   setup.system = syi;
3842
3843   /* options setup */
3844   soi = setup.options;
3845   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3846     setSetupInfo(options_setup_tokens, i,
3847                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3848   setup.options = soi;
3849 }
3850
3851 void LoadSetup()
3852 {
3853   char *filename = getSetupFilename();
3854   SetupFileHash *setup_file_hash = NULL;
3855
3856   /* always start with reliable default values */
3857   setSetupInfoToDefaults(&setup);
3858
3859   setup_file_hash = loadSetupFileHash(filename);
3860
3861   if (setup_file_hash)
3862   {
3863     char *player_name_new;
3864
3865     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3866     decodeSetupFileHash(setup_file_hash);
3867
3868     setup.direct_draw = !setup.double_buffering;
3869
3870     freeSetupFileHash(setup_file_hash);
3871
3872     /* needed to work around problems with fixed length strings */
3873     player_name_new = get_corrected_login_name(setup.player_name);
3874     free(setup.player_name);
3875     setup.player_name = player_name_new;
3876   }
3877   else
3878     Error(ERR_WARN, "using default setup values");
3879 }
3880
3881 void SaveSetup()
3882 {
3883   char *filename = getSetupFilename();
3884   FILE *file;
3885   int i, pnr;
3886
3887   InitUserDataDirectory();
3888
3889   if (!(file = fopen(filename, MODE_WRITE)))
3890   {
3891     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3892     return;
3893   }
3894
3895   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3896                                                getCookie("SETUP")));
3897   fprintf(file, "\n");
3898
3899   /* global setup */
3900   si = setup;
3901   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3902   {
3903     /* just to make things nicer :) */
3904     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3905         i == SETUP_TOKEN_GRAPHICS_SET)
3906       fprintf(file, "\n");
3907
3908     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3909   }
3910
3911   /* editor setup */
3912   sei = setup.editor;
3913   fprintf(file, "\n");
3914   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3915     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3916
3917   /* shortcut setup */
3918   ssi = setup.shortcut;
3919   fprintf(file, "\n");
3920   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3921     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3922
3923   /* player setup */
3924   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3925   {
3926     char prefix[30];
3927
3928     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3929     fprintf(file, "\n");
3930
3931     sii = setup.input[pnr];
3932     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3933       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3934   }
3935
3936   /* system setup */
3937   syi = setup.system;
3938   fprintf(file, "\n");
3939   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3940     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3941
3942   /* options setup */
3943   soi = setup.options;
3944   fprintf(file, "\n");
3945   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3946     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3947
3948   fclose(file);
3949
3950   SetFilePermissions(filename, PERMS_PRIVATE);
3951 }
3952
3953 void LoadCustomElementDescriptions()
3954 {
3955   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3956   SetupFileHash *setup_file_hash;
3957   int i;
3958
3959   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3960   {
3961     if (element_info[i].custom_description != NULL)
3962     {
3963       free(element_info[i].custom_description);
3964       element_info[i].custom_description = NULL;
3965     }
3966   }
3967
3968   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3969     return;
3970
3971   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3972   {
3973     char *token = getStringCat2(element_info[i].token_name, ".name");
3974     char *value = getHashEntry(setup_file_hash, token);
3975
3976     if (value != NULL)
3977       element_info[i].custom_description = getStringCopy(value);
3978
3979     free(token);
3980   }
3981
3982   freeSetupFileHash(setup_file_hash);
3983 }
3984
3985 void LoadSpecialMenuDesignSettings()
3986 {
3987   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3988   SetupFileHash *setup_file_hash;
3989   int i, j;
3990
3991   /* always start with reliable default values from default config */
3992   for (i = 0; image_config_vars[i].token != NULL; i++)
3993     for (j = 0; image_config[j].token != NULL; j++)
3994       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3995         *image_config_vars[i].value =
3996           get_auto_parameter_value(image_config_vars[i].token,
3997                                    image_config[j].value);
3998
3999   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4000     return;
4001
4002   /* special case: initialize with default values that may be overwritten */
4003   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4004   {
4005     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4006     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4007     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4008
4009     if (value_x != NULL)
4010       menu.draw_xoffset[i] = get_integer_from_string(value_x);
4011     if (value_y != NULL)
4012       menu.draw_yoffset[i] = get_integer_from_string(value_y);
4013     if (list_size != NULL)
4014       menu.list_size[i] = get_integer_from_string(list_size);
4015   }
4016
4017   /* read (and overwrite with) values that may be specified in config file */
4018   for (i = 0; image_config_vars[i].token != NULL; i++)
4019   {
4020     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4021
4022     if (value != NULL)
4023       *image_config_vars[i].value =
4024         get_auto_parameter_value(image_config_vars[i].token, value);
4025   }
4026
4027   freeSetupFileHash(setup_file_hash);
4028 }
4029
4030 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4031 {
4032   char *filename = getEditorSetupFilename();
4033   SetupFileList *setup_file_list, *list;
4034   SetupFileHash *element_hash;
4035   int num_unknown_tokens = 0;
4036   int i;
4037
4038   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4039     return;
4040
4041   element_hash = newSetupFileHash();
4042
4043   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4044     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4045
4046   /* determined size may be larger than needed (due to unknown elements) */
4047   *num_elements = 0;
4048   for (list = setup_file_list; list != NULL; list = list->next)
4049     (*num_elements)++;
4050
4051   /* add space for up to 3 more elements for padding that may be needed */
4052   *num_elements += 3;
4053
4054   *elements = checked_malloc(*num_elements * sizeof(int));
4055
4056   *num_elements = 0;
4057   for (list = setup_file_list; list != NULL; list = list->next)
4058   {
4059     char *value = getHashEntry(element_hash, list->token);
4060
4061     if (value)
4062     {
4063       (*elements)[(*num_elements)++] = atoi(value);
4064     }
4065     else
4066     {
4067       if (num_unknown_tokens == 0)
4068       {
4069         Error(ERR_RETURN_LINE, "-");
4070         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4071         Error(ERR_RETURN, "- config file: '%s'", filename);
4072
4073         num_unknown_tokens++;
4074       }
4075
4076       Error(ERR_RETURN, "- token: '%s'", list->token);
4077     }
4078   }
4079
4080   if (num_unknown_tokens > 0)
4081     Error(ERR_RETURN_LINE, "-");
4082
4083   while (*num_elements % 4)     /* pad with empty elements, if needed */
4084     (*elements)[(*num_elements)++] = EL_EMPTY;
4085
4086   freeSetupFileList(setup_file_list);
4087   freeSetupFileHash(element_hash);
4088
4089 #if 0
4090   /* TEST-ONLY */
4091   for (i = 0; i < *num_elements; i++)
4092     printf("editor: element '%s' [%d]\n",
4093            element_info[(*elements)[i]].token_name, (*elements)[i]);
4094 #endif
4095 }
4096
4097 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4098                                                      boolean is_sound)
4099 {
4100   SetupFileHash *setup_file_hash = NULL;
4101   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4102   char *filename_music, *filename_prefix, *filename_info;
4103   struct
4104   {
4105     char *token;
4106     char **value_ptr;
4107   }
4108   token_to_value_ptr[] =
4109   {
4110     { "title_header",   &tmp_music_file_info.title_header       },
4111     { "artist_header",  &tmp_music_file_info.artist_header      },
4112     { "album_header",   &tmp_music_file_info.album_header       },
4113     { "year_header",    &tmp_music_file_info.year_header        },
4114
4115     { "title",          &tmp_music_file_info.title              },
4116     { "artist",         &tmp_music_file_info.artist             },
4117     { "album",          &tmp_music_file_info.album              },
4118     { "year",           &tmp_music_file_info.year               },
4119
4120     { NULL,             NULL                                    },
4121   };
4122   int i;
4123
4124   filename_music = (is_sound ? getCustomSoundFilename(basename) :
4125                     getCustomMusicFilename(basename));
4126
4127   if (filename_music == NULL)
4128     return NULL;
4129
4130   /* ---------- try to replace file extension ---------- */
4131
4132   filename_prefix = getStringCopy(filename_music);
4133   if (strrchr(filename_prefix, '.') != NULL)
4134     *strrchr(filename_prefix, '.') = '\0';
4135   filename_info = getStringCat2(filename_prefix, ".txt");
4136
4137 #if 0
4138   printf("trying to load file '%s'...\n", filename_info);
4139 #endif
4140
4141   if (fileExists(filename_info))
4142     setup_file_hash = loadSetupFileHash(filename_info);
4143
4144   free(filename_prefix);
4145   free(filename_info);
4146
4147   if (setup_file_hash == NULL)
4148   {
4149     /* ---------- try to add file extension ---------- */
4150
4151     filename_prefix = getStringCopy(filename_music);
4152     filename_info = getStringCat2(filename_prefix, ".txt");
4153
4154 #if 0
4155     printf("trying to load file '%s'...\n", filename_info);
4156 #endif
4157
4158     if (fileExists(filename_info))
4159       setup_file_hash = loadSetupFileHash(filename_info);
4160
4161     free(filename_prefix);
4162     free(filename_info);
4163   }
4164
4165   if (setup_file_hash == NULL)
4166     return NULL;
4167
4168   /* ---------- music file info found ---------- */
4169
4170   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4171
4172   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4173   {
4174     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4175
4176     *token_to_value_ptr[i].value_ptr =
4177       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4178   }
4179
4180   tmp_music_file_info.basename = getStringCopy(basename);
4181   tmp_music_file_info.music = music;
4182   tmp_music_file_info.is_sound = is_sound;
4183
4184   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4185   *new_music_file_info = tmp_music_file_info;
4186
4187   return new_music_file_info;
4188 }
4189
4190 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4191 {
4192   return get_music_file_info_ext(basename, music, FALSE);
4193 }
4194
4195 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4196 {
4197   return get_music_file_info_ext(basename, sound, TRUE);
4198 }
4199
4200 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4201                                      char *basename, boolean is_sound)
4202 {
4203   for (; list != NULL; list = list->next)
4204     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4205       return TRUE;
4206
4207   return FALSE;
4208 }
4209
4210 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4211 {
4212   return music_info_listed_ext(list, basename, FALSE);
4213 }
4214
4215 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4216 {
4217   return music_info_listed_ext(list, basename, TRUE);
4218 }
4219
4220 void LoadMusicInfo()
4221 {
4222   char *music_directory = getCustomMusicDirectory();
4223   int num_music = getMusicListSize();
4224   int num_music_noconf = 0;
4225   int num_sounds = getSoundListSize();
4226   DIR *dir;
4227   struct dirent *dir_entry;
4228   struct FileInfo *music, *sound;
4229   struct MusicFileInfo *next, **new;
4230   int i;
4231
4232   while (music_file_info != NULL)
4233   {
4234     next = music_file_info->next;
4235
4236     checked_free(music_file_info->basename);
4237
4238     checked_free(music_file_info->title_header);
4239     checked_free(music_file_info->artist_header);
4240     checked_free(music_file_info->album_header);
4241     checked_free(music_file_info->year_header);
4242
4243     checked_free(music_file_info->title);
4244     checked_free(music_file_info->artist);
4245     checked_free(music_file_info->album);
4246     checked_free(music_file_info->year);
4247
4248     free(music_file_info);
4249
4250     music_file_info = next;
4251   }
4252
4253   new = &music_file_info;
4254
4255 #if 0
4256   printf("::: num_music == %d\n", num_music);
4257 #endif
4258
4259   for (i = 0; i < num_music; i++)
4260   {
4261     music = getMusicListEntry(i);
4262
4263 #if 0
4264     printf("::: %d [%08x]\n", i, music->filename);
4265 #endif
4266
4267     if (music->filename == NULL)
4268       continue;
4269
4270     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4271       continue;
4272
4273     /* a configured file may be not recognized as music */
4274     if (!FileIsMusic(music->filename))
4275       continue;
4276
4277 #if 0
4278     printf("::: -> '%s' (configured)\n", music->filename);
4279 #endif
4280
4281     if (!music_info_listed(music_file_info, music->filename))
4282     {
4283       *new = get_music_file_info(music->filename, i);
4284       if (*new != NULL)
4285         new = &(*new)->next;
4286     }
4287   }
4288
4289   if ((dir = opendir(music_directory)) == NULL)
4290   {
4291     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4292     return;
4293   }
4294
4295   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
4296   {
4297     char *basename = dir_entry->d_name;
4298     boolean music_already_used = FALSE;
4299     int i;
4300
4301     /* skip all music files that are configured in music config file */
4302     for (i = 0; i < num_music; i++)
4303     {
4304       music = getMusicListEntry(i);
4305
4306       if (music->filename == NULL)
4307         continue;
4308
4309       if (strcmp(basename, music->filename) == 0)
4310       {
4311         music_already_used = TRUE;
4312         break;
4313       }
4314     }
4315
4316     if (music_already_used)
4317       continue;
4318
4319     if (!FileIsMusic(basename))
4320       continue;
4321
4322 #if 0
4323     printf("::: -> '%s' (found in directory)\n", basename);
4324 #endif
4325
4326     if (!music_info_listed(music_file_info, basename))
4327     {
4328       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4329       if (*new != NULL)
4330         new = &(*new)->next;
4331     }
4332
4333     num_music_noconf++;
4334   }
4335
4336   closedir(dir);
4337
4338   for (i = 0; i < num_sounds; i++)
4339   {
4340     sound = getSoundListEntry(i);
4341
4342     if (sound->filename == NULL)
4343       continue;
4344
4345     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4346       continue;
4347
4348     /* a configured file may be not recognized as sound */
4349     if (!FileIsSound(sound->filename))
4350       continue;
4351
4352 #if 0
4353     printf("::: -> '%s' (configured)\n", sound->filename);
4354 #endif
4355
4356     if (!sound_info_listed(music_file_info, sound->filename))
4357     {
4358       *new = get_sound_file_info(sound->filename, i);
4359       if (*new != NULL)
4360         new = &(*new)->next;
4361     }
4362   }
4363
4364 #if 0
4365   /* TEST-ONLY */
4366   for (next = music_file_info; next != NULL; next = next->next)
4367     printf("::: title == '%s'\n", next->title);
4368 #endif
4369 }
4370
4371 void add_helpanim_entry(int element, int action, int direction, int delay,
4372                         int *num_list_entries)
4373 {
4374   struct HelpAnimInfo *new_list_entry;
4375   (*num_list_entries)++;
4376
4377   helpanim_info =
4378     checked_realloc(helpanim_info,
4379                     *num_list_entries * sizeof(struct HelpAnimInfo));
4380   new_list_entry = &helpanim_info[*num_list_entries - 1];
4381
4382   new_list_entry->element = element;
4383   new_list_entry->action = action;
4384   new_list_entry->direction = direction;
4385   new_list_entry->delay = delay;
4386 }
4387
4388 void print_unknown_token(char *filename, char *token, int token_nr)
4389 {
4390   if (token_nr == 0)
4391   {
4392     Error(ERR_RETURN_LINE, "-");
4393     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4394     Error(ERR_RETURN, "- config file: '%s'", filename);
4395   }
4396
4397   Error(ERR_RETURN, "- token: '%s'", token);
4398 }
4399
4400 void print_unknown_token_end(int token_nr)
4401 {
4402   if (token_nr > 0)
4403     Error(ERR_RETURN_LINE, "-");
4404 }
4405
4406 void LoadHelpAnimInfo()
4407 {
4408   char *filename = getHelpAnimFilename();
4409   SetupFileList *setup_file_list = NULL, *list;
4410   SetupFileHash *element_hash, *action_hash, *direction_hash;
4411   int num_list_entries = 0;
4412   int num_unknown_tokens = 0;
4413   int i;
4414
4415   if (fileExists(filename))
4416     setup_file_list = loadSetupFileList(filename);
4417
4418   if (setup_file_list == NULL)
4419   {
4420     /* use reliable default values from static configuration */
4421     SetupFileList *insert_ptr;
4422
4423     insert_ptr = setup_file_list =
4424       newSetupFileList(helpanim_config[0].token,
4425                        helpanim_config[0].value);
4426
4427     for (i = 1; helpanim_config[i].token; i++)
4428       insert_ptr = addListEntry(insert_ptr,
4429                                 helpanim_config[i].token,
4430                                 helpanim_config[i].value);
4431   }
4432
4433   element_hash   = newSetupFileHash();
4434   action_hash    = newSetupFileHash();
4435   direction_hash = newSetupFileHash();
4436
4437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4438     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4439
4440   for (i = 0; i < NUM_ACTIONS; i++)
4441     setHashEntry(action_hash, element_action_info[i].suffix,
4442                  i_to_a(element_action_info[i].value));
4443
4444   /* do not store direction index (bit) here, but direction value! */
4445   for (i = 0; i < NUM_DIRECTIONS; i++)
4446     setHashEntry(direction_hash, element_direction_info[i].suffix,
4447                  i_to_a(1 << element_direction_info[i].value));
4448
4449   for (list = setup_file_list; list != NULL; list = list->next)
4450   {
4451     char *element_token, *action_token, *direction_token;
4452     char *element_value, *action_value, *direction_value;
4453     int delay = atoi(list->value);
4454
4455     if (strcmp(list->token, "end") == 0)
4456     {
4457       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4458
4459       continue;
4460     }
4461
4462     /* first try to break element into element/action/direction parts;
4463        if this does not work, also accept combined "element[.act][.dir]"
4464        elements (like "dynamite.active"), which are unique elements */
4465
4466     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
4467     {
4468       element_value = getHashEntry(element_hash, list->token);
4469       if (element_value != NULL)        /* element found */
4470         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4471                            &num_list_entries);
4472       else
4473       {
4474         /* no further suffixes found -- this is not an element */
4475         print_unknown_token(filename, list->token, num_unknown_tokens++);
4476       }
4477
4478       continue;
4479     }
4480
4481     /* token has format "<prefix>.<something>" */
4482
4483     action_token = strchr(list->token, '.');    /* suffix may be action ... */
4484     direction_token = action_token;             /* ... or direction */
4485
4486     element_token = getStringCopy(list->token);
4487     *strchr(element_token, '.') = '\0';
4488
4489     element_value = getHashEntry(element_hash, element_token);
4490
4491     if (element_value == NULL)          /* this is no element */
4492     {
4493       element_value = getHashEntry(element_hash, list->token);
4494       if (element_value != NULL)        /* combined element found */
4495         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4496                            &num_list_entries);
4497       else
4498         print_unknown_token(filename, list->token, num_unknown_tokens++);
4499
4500       free(element_token);
4501
4502       continue;
4503     }
4504
4505     action_value = getHashEntry(action_hash, action_token);
4506
4507     if (action_value != NULL)           /* action found */
4508     {
4509       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4510                     &num_list_entries);
4511
4512       free(element_token);
4513
4514       continue;
4515     }
4516
4517     direction_value = getHashEntry(direction_hash, direction_token);
4518
4519     if (direction_value != NULL)        /* direction found */
4520     {
4521       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4522                          &num_list_entries);
4523
4524       free(element_token);
4525
4526       continue;
4527     }
4528
4529     if (strchr(action_token + 1, '.') == NULL)
4530     {
4531       /* no further suffixes found -- this is not an action nor direction */
4532
4533       element_value = getHashEntry(element_hash, list->token);
4534       if (element_value != NULL)        /* combined element found */
4535         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4536                            &num_list_entries);
4537       else
4538         print_unknown_token(filename, list->token, num_unknown_tokens++);
4539
4540       free(element_token);
4541
4542       continue;
4543     }
4544
4545     /* token has format "<prefix>.<suffix>.<something>" */
4546
4547     direction_token = strchr(action_token + 1, '.');
4548
4549     action_token = getStringCopy(action_token);
4550     *strchr(action_token + 1, '.') = '\0';
4551
4552     action_value = getHashEntry(action_hash, action_token);
4553
4554     if (action_value == NULL)           /* this is no action */
4555     {
4556       element_value = getHashEntry(element_hash, list->token);
4557       if (element_value != NULL)        /* combined element found */
4558         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4559                            &num_list_entries);
4560       else
4561         print_unknown_token(filename, list->token, num_unknown_tokens++);
4562
4563       free(element_token);
4564       free(action_token);
4565
4566       continue;
4567     }
4568
4569     direction_value = getHashEntry(direction_hash, direction_token);
4570
4571     if (direction_value != NULL)        /* direction found */
4572     {
4573       add_helpanim_entry(atoi(element_value), atoi(action_value),
4574                          atoi(direction_value), delay, &num_list_entries);
4575
4576       free(element_token);
4577       free(action_token);
4578
4579       continue;
4580     }
4581
4582     /* this is no direction */
4583
4584     element_value = getHashEntry(element_hash, list->token);
4585     if (element_value != NULL)          /* combined element found */
4586       add_helpanim_entry(atoi(element_value), -1, -1, delay,
4587                          &num_list_entries);
4588     else
4589       print_unknown_token(filename, list->token, num_unknown_tokens++);
4590
4591     free(element_token);
4592     free(action_token);
4593   }
4594
4595   print_unknown_token_end(num_unknown_tokens);
4596
4597   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4598   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
4599
4600   freeSetupFileList(setup_file_list);
4601   freeSetupFileHash(element_hash);
4602   freeSetupFileHash(action_hash);
4603   freeSetupFileHash(direction_hash);
4604
4605 #if 0
4606   /* TEST ONLY */
4607   for (i = 0; i < num_list_entries; i++)
4608     printf("::: %d, %d, %d => %d\n",
4609            helpanim_info[i].element,
4610            helpanim_info[i].action,
4611            helpanim_info[i].direction,
4612            helpanim_info[i].delay);
4613 #endif
4614 }
4615
4616 void LoadHelpTextInfo()
4617 {
4618   char *filename = getHelpTextFilename();
4619   int i;
4620
4621   if (helptext_info != NULL)
4622   {
4623     freeSetupFileHash(helptext_info);
4624     helptext_info = NULL;
4625   }
4626
4627   if (fileExists(filename))
4628     helptext_info = loadSetupFileHash(filename);
4629
4630   if (helptext_info == NULL)
4631   {
4632     /* use reliable default values from static configuration */
4633     helptext_info = newSetupFileHash();
4634
4635     for (i = 0; helptext_config[i].token; i++)
4636       setHashEntry(helptext_info,
4637                    helptext_config[i].token,
4638                    helptext_config[i].value);
4639   }
4640
4641 #if 0
4642   /* TEST ONLY */
4643   BEGIN_HASH_ITERATION(helptext_info, itr)
4644   {
4645     printf("::: '%s' => '%s'\n",
4646            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4647   }
4648   END_HASH_ITERATION(hash, itr)
4649 #endif
4650 }