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