1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
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 */
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) (96 + (x) * 48)
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"
52 /* values for "CONF" chunk */
53 #define CONF_MASK_1_BYTE 0x00
54 #define CONF_MASK_2_BYTE 0x40
55 #define CONF_MASK_4_BYTE 0x80
56 #define CONF_MASK_MULTI_BYTES 0xc0
58 #define CONF_MASK_BYTES 0xc0
59 #define CONF_MASK_TOKEN 0x3f
61 #define CONF_LAST_ENTRY (CONF_MASK_1_BYTE | 0)
63 #define CONF_VALUE_INTEGER_1 (CONF_MASK_1_BYTE | 1)
64 #define CONF_VALUE_INTEGER_2 (CONF_MASK_1_BYTE | 2)
65 #define CONF_VALUE_INTEGER_3 (CONF_MASK_1_BYTE | 3)
66 #define CONF_VALUE_INTEGER_4 (CONF_MASK_1_BYTE | 4)
67 #define CONF_VALUE_BOOLEAN_1 (CONF_MASK_1_BYTE | 5)
68 #define CONF_VALUE_BOOLEAN_2 (CONF_MASK_1_BYTE | 6)
69 #define CONF_VALUE_BOOLEAN_3 (CONF_MASK_1_BYTE | 7)
70 #define CONF_VALUE_BOOLEAN_4 (CONF_MASK_1_BYTE | 8)
72 #define CONF_VALUE_ELEMENT_1 (CONF_MASK_2_BYTE | 1)
73 #define CONF_VALUE_ELEMENT_2 (CONF_MASK_2_BYTE | 2)
74 #define CONF_VALUE_ELEMENT_3 (CONF_MASK_2_BYTE | 3)
75 #define CONF_VALUE_ELEMENT_4 (CONF_MASK_2_BYTE | 4)
77 #define CONF_VALUE_CONTENT_1 (CONF_MASK_MULTI_BYTES | 1)
78 #define CONF_VALUE_CONTENT_8 (CONF_MASK_MULTI_BYTES | 2)
80 #define CONF_VALUE_INTEGER(x) ((x) >= CONF_VALUE_INTEGER_1 && \
81 (x) <= CONF_VALUE_INTEGER_4)
83 #define CONF_VALUE_BOOLEAN(x) ((x) >= CONF_VALUE_BOOLEAN_1 && \
84 (x) <= CONF_VALUE_BOOLEAN_4)
86 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
87 (x) == CONF_MASK_2_BYTE ? 2 : \
88 (x) == CONF_MASK_4_BYTE ? 4 : 0)
90 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
91 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
93 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
95 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * 2)
96 #define CONF_CONTENT_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)] << 8)|\
97 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
99 static struct LevelInfo li;
109 /* ---------- 1-byte values ---------------------------------------------- */
111 EL_EMC_ANDROID, CONF_VALUE_INTEGER_1,
112 &li.android_move_time, 10
115 EL_EMC_ANDROID, CONF_VALUE_INTEGER_2,
116 &li.android_clone_time, 10
119 EL_EMC_MAGIC_BALL, CONF_VALUE_INTEGER_1,
123 EL_EMC_LENSES, CONF_VALUE_INTEGER_1,
127 EL_EMC_LENSES, CONF_VALUE_INTEGER_2,
131 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_1,
132 &li.magnify_score, 10
135 EL_EMC_MAGNIFIER, CONF_VALUE_INTEGER_2,
139 EL_ROBOT, CONF_VALUE_INTEGER_1,
143 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_1,
144 &li.game_of_life[0], 2
147 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_2,
148 &li.game_of_life[1], 3
151 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_3,
152 &li.game_of_life[2], 3
155 EL_GAME_OF_LIFE, CONF_VALUE_INTEGER_4,
156 &li.game_of_life[3], 3
159 EL_BIOMAZE, CONF_VALUE_INTEGER_1,
163 EL_BIOMAZE, CONF_VALUE_INTEGER_2,
167 EL_BIOMAZE, CONF_VALUE_INTEGER_3,
171 EL_BIOMAZE, CONF_VALUE_INTEGER_4,
175 EL_BALLOON, CONF_VALUE_INTEGER_1,
176 &li.wind_direction_initial, MV_NONE
179 EL_TIMEGATE_SWITCH, CONF_VALUE_INTEGER_1,
180 &li.time_timegate, 10
183 EL_LIGHT_SWITCH_ACTIVE, CONF_VALUE_INTEGER_1,
187 EL_SHIELD_NORMAL, CONF_VALUE_INTEGER_1,
188 &li.shield_normal_time, 10
191 EL_SHIELD_DEADLY, CONF_VALUE_INTEGER_1,
192 &li.shield_deadly_time, 10
195 EL_EXTRA_TIME, CONF_VALUE_INTEGER_1,
199 EL_TIME_ORB_FULL, CONF_VALUE_INTEGER_1,
200 &li.time_orb_time, 10
203 EL_TIME_ORB_FULL, CONF_VALUE_BOOLEAN_1,
204 &li.use_time_orb_bug, FALSE
207 EL_PLAYER_1, CONF_VALUE_BOOLEAN_1,
208 &li.block_snap_field, TRUE
211 EL_PLAYER_1, CONF_VALUE_BOOLEAN_2,
212 &li.use_start_element[0], FALSE
215 EL_PLAYER_2, CONF_VALUE_ELEMENT_2,
216 &li.use_start_element[1], FALSE
219 EL_PLAYER_3, CONF_VALUE_ELEMENT_2,
220 &li.use_start_element[2], FALSE
223 EL_PLAYER_4, CONF_VALUE_ELEMENT_2,
224 &li.use_start_element[3], FALSE
227 /* ---------- 2-byte values ---------------------------------------------- */
229 EL_PLAYER_1, CONF_VALUE_ELEMENT_1,
230 &li.start_element[0], EL_PLAYER_1
233 EL_PLAYER_2, CONF_VALUE_ELEMENT_1,
234 &li.start_element[1], EL_PLAYER_2
237 EL_PLAYER_3, CONF_VALUE_ELEMENT_1,
238 &li.start_element[2], EL_PLAYER_3
241 EL_PLAYER_4, CONF_VALUE_ELEMENT_1,
242 &li.start_element[3], EL_PLAYER_4
245 /* ---------- multi-byte values ------------------------------------------ */
247 EL_EMC_MAGIC_BALL, CONF_VALUE_CONTENT_8,
248 &li.ball_content, EL_EMPTY
264 { LEVEL_FILE_TYPE_RND, "RND" },
265 { LEVEL_FILE_TYPE_BD, "BD" },
266 { LEVEL_FILE_TYPE_EM, "EM" },
267 { LEVEL_FILE_TYPE_SP, "SP" },
268 { LEVEL_FILE_TYPE_DX, "DX" },
269 { LEVEL_FILE_TYPE_SB, "SB" },
270 { LEVEL_FILE_TYPE_DC, "DC" },
275 /* ========================================================================= */
276 /* level file functions */
277 /* ========================================================================= */
279 static void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
283 li = *level; /* copy level information into temporary buffer */
285 for (i = 0; element_conf[i].element != -1; i++)
287 int default_value = element_conf[i].default_value;
288 int type = element_conf[i].type;
289 int bytes = type & CONF_MASK_BYTES;
291 if (bytes != CONF_MASK_MULTI_BYTES)
293 if (CONF_VALUE_BOOLEAN(type))
294 *(boolean *)(element_conf[i].value) = default_value;
296 *(int *) (element_conf[i].value) = default_value;
298 else if (type == CONF_VALUE_CONTENT_8)
300 struct Content *content = (struct Content *)(element_conf[i].value);
303 for (c = 0; c < MAX_ELEMENT_CONTENTS; c++)
304 for (y = 0; y < 3; y++)
305 for (x = 0; x < 3; x++)
306 content[c].e[x][y] = default_value;
310 *level = li; /* copy temporary buffer back to level information */
313 void setElementChangePages(struct ElementInfo *ei, int change_pages)
315 int change_page_size = sizeof(struct ElementChangeInfo);
317 ei->num_change_pages = MAX(1, change_pages);
320 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
322 if (ei->current_change_page >= ei->num_change_pages)
323 ei->current_change_page = ei->num_change_pages - 1;
325 ei->change = &ei->change_page[ei->current_change_page];
328 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
332 change->can_change = FALSE;
334 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
335 change->has_event[i] = FALSE;
337 change->trigger_player = CH_PLAYER_ANY;
338 change->trigger_side = CH_SIDE_ANY;
339 change->trigger_page = CH_PAGE_ANY;
341 change->target_element = EL_EMPTY_SPACE;
343 change->delay_fixed = 0;
344 change->delay_random = 0;
345 change->delay_frames = FRAMES_PER_SECOND;
347 change->trigger_element = EL_EMPTY_SPACE;
349 change->explode = FALSE;
350 change->use_target_content = FALSE;
351 change->only_if_complete = FALSE;
352 change->use_random_replace = FALSE;
353 change->random_percentage = 100;
354 change->replace_when = CP_WHEN_EMPTY;
356 change->has_action = FALSE;
357 change->action_type = CA_NO_ACTION;
358 change->action_mode = CA_MODE_UNDEFINED;
359 change->action_arg = CA_ARG_UNDEFINED;
361 for (x = 0; x < 3; x++)
362 for (y = 0; y < 3; y++)
363 change->target_content.e[x][y] = EL_EMPTY_SPACE;
365 change->direct_action = 0;
366 change->other_action = 0;
368 change->pre_change_function = NULL;
369 change->change_function = NULL;
370 change->post_change_function = NULL;
373 static void setLevelInfoToDefaults(struct LevelInfo *level)
375 static boolean clipboard_elements_initialized = FALSE;
378 setLevelInfoToDefaultsFromConfigList(level);
379 setLevelInfoToDefaults_EM();
381 level->native_em_level = &native_em_level;
383 level->game_engine_type = GAME_ENGINE_TYPE_RND;
385 level->file_version = FILE_VERSION_ACTUAL;
386 level->game_version = GAME_VERSION_ACTUAL;
388 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
389 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
390 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
392 level->fieldx = STD_LEV_FIELDX;
393 level->fieldy = STD_LEV_FIELDY;
395 for (x = 0; x < MAX_LEV_FIELDX; x++)
396 for (y = 0; y < MAX_LEV_FIELDY; y++)
397 level->field[x][y] = EL_SAND;
400 level->gems_needed = 0;
402 level->amoeba_speed = 10;
404 level->time_magic_wall = 10;
405 level->time_wheel = 10;
407 level->time_light = 10;
408 level->time_timegate = 10;
411 level->amoeba_content = EL_DIAMOND;
413 level->game_of_life[0] = 2;
414 level->game_of_life[1] = 3;
415 level->game_of_life[2] = 3;
416 level->game_of_life[3] = 3;
418 level->biomaze[0] = 2;
419 level->biomaze[1] = 3;
420 level->biomaze[2] = 3;
421 level->biomaze[3] = 3;
423 level->double_speed = FALSE;
424 level->initial_gravity = FALSE;
425 level->em_slippery_gems = FALSE;
426 level->instant_relocation = FALSE;
427 level->can_pass_to_walkable = FALSE;
428 level->grow_into_diggable = TRUE;
430 level->block_snap_field = TRUE;
432 level->block_last_field = FALSE; /* EM does not block by default */
433 level->sp_block_last_field = TRUE; /* SP blocks the last field */
435 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
436 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
438 level->use_spring_bug = FALSE;
439 level->use_time_orb_bug = FALSE;
441 level->use_step_counter = FALSE;
443 /* values for the new EMC elements */
445 level->android_move_time = 10;
446 level->android_clone_time = 10;
447 level->ball_time = 10;
448 level->lenses_score = 10;
449 level->lenses_time = 10;
450 level->magnify_score = 10;
451 level->magnify_time = 10;
452 level->slurp_score = 10;
453 level->wind_direction_initial = MV_NONE;
455 level->ball_random = FALSE;
456 level->ball_state_initial = FALSE;
458 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
459 for (x = 0; x < 3; x++)
460 for (y = 0; y < 3; y++)
461 level->ball_content[i].e[x][y] = EL_EMPTY;
463 for (i = 0; i < 16; i++)
464 level->android_array[i] = FALSE;
466 level->use_custom_template = FALSE;
468 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
469 level->name[i] = '\0';
470 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
471 level->author[i] = '\0';
473 strcpy(level->name, NAMELESS_LEVEL_NAME);
474 strcpy(level->author, ANONYMOUS_NAME);
476 for (i = 0; i < 4; i++)
478 level->envelope_text[i][0] = '\0';
479 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
480 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
483 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
484 level->score[i] = 10;
486 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
487 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
488 for (x = 0; x < 3; x++)
489 for (y = 0; y < 3; y++)
490 level->yamyam_content[i].e[x][y] =
491 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
493 level->field[0][0] = EL_PLAYER_1;
494 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
496 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
499 struct ElementInfo *ei = &element_info[element];
501 /* never initialize clipboard elements after the very first time */
502 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
505 setElementChangePages(ei, 1);
506 setElementChangeInfoToDefaults(ei->change);
508 if (IS_CUSTOM_ELEMENT(element) ||
509 IS_GROUP_ELEMENT(element) ||
510 IS_INTERNAL_ELEMENT(element))
512 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
513 ei->description[j] = '\0';
515 if (ei->custom_description != NULL)
516 strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
518 strcpy(ei->description, ei->editor_description);
520 ei->use_gfx_element = FALSE;
521 ei->gfx_element = EL_EMPTY_SPACE;
523 ei->modified_settings = FALSE;
526 if (IS_CUSTOM_ELEMENT(element) ||
527 IS_INTERNAL_ELEMENT(element))
529 ei->access_direction = MV_ALL_DIRECTIONS;
531 ei->collect_score_initial = 10; /* special default */
532 ei->collect_count_initial = 1; /* special default */
534 ei->ce_value_fixed_initial = 0;
535 ei->ce_value_random_initial = 0;
536 ei->use_last_ce_value = FALSE;
538 ei->push_delay_fixed = -1; /* initialize later */
539 ei->push_delay_random = -1; /* initialize later */
540 ei->drop_delay_fixed = 0;
541 ei->drop_delay_random = 0;
542 ei->move_delay_fixed = 0;
543 ei->move_delay_random = 0;
545 ei->move_pattern = MV_ALL_DIRECTIONS;
546 ei->move_direction_initial = MV_START_AUTOMATIC;
547 ei->move_stepsize = TILEX / 8;
549 ei->move_enter_element = EL_EMPTY_SPACE;
550 ei->move_leave_element = EL_EMPTY_SPACE;
551 ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
553 ei->slippery_type = SLIPPERY_ANY_RANDOM;
555 ei->explosion_type = EXPLODES_3X3;
556 ei->explosion_delay = 16;
557 ei->ignition_delay = 8;
559 for (x = 0; x < 3; x++)
560 for (y = 0; y < 3; y++)
561 ei->content.e[x][y] = EL_EMPTY_SPACE;
564 ei->access_layer = 0;
565 ei->access_protected = 0;
566 ei->walk_to_action = 0;
567 ei->smash_targets = 0;
570 ei->can_explode_by_fire = FALSE;
571 ei->can_explode_smashed = FALSE;
572 ei->can_explode_impact = FALSE;
574 ei->current_change_page = 0;
576 /* start with no properties at all */
577 for (j = 0; j < NUM_EP_BITFIELDS; j++)
578 Properties[element][j] = EP_BITMASK_DEFAULT;
580 /* now set default properties */
581 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
584 if (IS_GROUP_ELEMENT(element) ||
585 IS_INTERNAL_ELEMENT(element))
587 struct ElementGroupInfo *group;
589 /* initialize memory for list of elements in group */
590 if (ei->group == NULL)
591 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
595 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
596 group->element[j] = EL_EMPTY_SPACE;
598 /* default: only one element in group */
599 group->num_elements = 1;
601 group->choice_mode = ANIM_RANDOM;
605 clipboard_elements_initialized = TRUE;
607 BorderElement = EL_STEELWALL;
609 level->no_valid_file = FALSE;
611 level->changed = FALSE;
613 if (leveldir_current == NULL) /* only when dumping level */
616 /* try to determine better author name than 'anonymous' */
617 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
619 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
620 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
624 switch (LEVELCLASS(leveldir_current))
626 case LEVELCLASS_TUTORIAL:
627 strcpy(level->author, PROGRAM_AUTHOR_STRING);
630 case LEVELCLASS_CONTRIB:
631 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
632 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
635 case LEVELCLASS_PRIVATE:
636 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
637 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
641 /* keep default value */
647 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
649 level_file_info->nr = 0;
650 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
651 level_file_info->packed = FALSE;
652 level_file_info->basename = NULL;
653 level_file_info->filename = NULL;
656 static void ActivateLevelTemplate()
658 /* Currently there is no special action needed to activate the template
659 data, because 'element_info' and 'Properties' overwrite the original
660 level data, while all other variables do not change. */
663 static char *getLevelFilenameFromBasename(char *basename)
665 static char *filename = NULL;
667 checked_free(filename);
669 filename = getPath2(getCurrentLevelDir(), basename);
674 static int getFileTypeFromBasename(char *basename)
676 static char *filename = NULL;
677 struct stat file_status;
679 /* ---------- try to determine file type from filename ---------- */
681 /* check for typical filename of a Supaplex level package file */
682 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
683 strncmp(basename, "LEVELS.D", 8) == 0))
684 return LEVEL_FILE_TYPE_SP;
686 /* ---------- try to determine file type from filesize ---------- */
688 checked_free(filename);
689 filename = getPath2(getCurrentLevelDir(), basename);
691 if (stat(filename, &file_status) == 0)
693 /* check for typical filesize of a Supaplex level package file */
694 if (file_status.st_size == 170496)
695 return LEVEL_FILE_TYPE_SP;
698 return LEVEL_FILE_TYPE_UNKNOWN;
701 static char *getSingleLevelBasename(int nr)
703 static char basename[MAX_FILENAME_LEN];
706 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
708 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
713 static char *getPackedLevelBasename(int type)
715 static char basename[MAX_FILENAME_LEN];
716 char *directory = getCurrentLevelDir();
718 struct dirent *dir_entry;
720 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
722 if ((dir = opendir(directory)) == NULL)
724 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
729 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
731 char *entry_basename = dir_entry->d_name;
732 int entry_type = getFileTypeFromBasename(entry_basename);
734 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
736 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
739 strcpy(basename, entry_basename);
751 static char *getSingleLevelFilename(int nr)
753 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
757 static char *getPackedLevelFilename(int type)
759 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
763 char *getDefaultLevelFilename(int nr)
765 return getSingleLevelFilename(nr);
769 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
774 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
775 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
779 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
780 int type, char *format, ...)
782 static char basename[MAX_FILENAME_LEN];
785 va_start(ap, format);
786 vsprintf(basename, format, ap);
791 lfi->basename = basename;
792 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
795 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
800 lfi->basename = getPackedLevelBasename(lfi->type);
801 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
804 static int getFiletypeFromID(char *filetype_id)
806 char *filetype_id_lower;
807 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
810 if (filetype_id == NULL)
811 return LEVEL_FILE_TYPE_UNKNOWN;
813 filetype_id_lower = getStringToLower(filetype_id);
815 for (i = 0; filetype_id_list[i].id != NULL; i++)
817 char *id_lower = getStringToLower(filetype_id_list[i].id);
819 if (strcmp(filetype_id_lower, id_lower) == 0)
820 filetype = filetype_id_list[i].filetype;
824 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
828 free(filetype_id_lower);
833 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
837 /* special case: level number is negative => check for level template file */
840 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
841 "template.%s", LEVELFILE_EXTENSION);
843 /* no fallback if template file not existing */
847 /* special case: check for file name/pattern specified in "levelinfo.conf" */
848 if (leveldir_current->level_filename != NULL)
850 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
852 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
853 leveldir_current->level_filename, nr);
854 if (fileExists(lfi->filename))
858 /* check for native Rocks'n'Diamonds level file */
859 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
860 "%03d.%s", nr, LEVELFILE_EXTENSION);
861 if (fileExists(lfi->filename))
864 /* check for Emerald Mine level file (V1) */
865 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
866 'a' + (nr / 10) % 26, '0' + nr % 10);
867 if (fileExists(lfi->filename))
869 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
870 'A' + (nr / 10) % 26, '0' + nr % 10);
871 if (fileExists(lfi->filename))
874 /* check for Emerald Mine level file (V2 to V5) */
875 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
876 if (fileExists(lfi->filename))
879 /* check for Emerald Mine level file (V6 / single mode) */
880 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
881 if (fileExists(lfi->filename))
883 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
884 if (fileExists(lfi->filename))
887 /* check for Emerald Mine level file (V6 / teamwork mode) */
888 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
889 if (fileExists(lfi->filename))
891 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
892 if (fileExists(lfi->filename))
895 /* check for various packed level file formats */
896 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
897 if (fileExists(lfi->filename))
900 /* no known level file found -- use default values (and fail later) */
901 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
902 "%03d.%s", nr, LEVELFILE_EXTENSION);
905 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
907 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
908 lfi->type = getFileTypeFromBasename(lfi->basename);
911 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
913 /* always start with reliable default values */
914 setFileInfoToDefaults(level_file_info);
916 level_file_info->nr = nr; /* set requested level number */
918 determineLevelFileInfo_Filename(level_file_info);
919 determineLevelFileInfo_Filetype(level_file_info);
922 /* ------------------------------------------------------------------------- */
923 /* functions for loading R'n'D level */
924 /* ------------------------------------------------------------------------- */
926 int getMappedElement(int element)
928 /* remap some (historic, now obsolete) elements */
932 case EL_PLAYER_OBSOLETE:
933 element = EL_PLAYER_1;
936 case EL_KEY_OBSOLETE:
939 case EL_EM_KEY_1_FILE_OBSOLETE:
940 element = EL_EM_KEY_1;
943 case EL_EM_KEY_2_FILE_OBSOLETE:
944 element = EL_EM_KEY_2;
947 case EL_EM_KEY_3_FILE_OBSOLETE:
948 element = EL_EM_KEY_3;
951 case EL_EM_KEY_4_FILE_OBSOLETE:
952 element = EL_EM_KEY_4;
955 case EL_ENVELOPE_OBSOLETE:
956 element = EL_ENVELOPE_1;
964 if (element >= NUM_FILE_ELEMENTS)
966 Error(ERR_WARN, "invalid level element %d", element);
968 element = EL_UNKNOWN;
976 int getMappedElementByVersion(int element, int game_version)
978 /* remap some elements due to certain game version */
980 if (game_version <= VERSION_IDENT(2,2,0,0))
982 /* map game font elements */
983 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
984 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
985 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
986 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
989 if (game_version < VERSION_IDENT(3,0,0,0))
991 /* map Supaplex gravity tube elements */
992 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
993 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
994 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
995 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1002 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
1004 level->file_version = getFileVersion(file);
1005 level->game_version = getFileVersion(file);
1010 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
1014 level->fieldx = getFile8Bit(file);
1015 level->fieldy = getFile8Bit(file);
1017 level->time = getFile16BitBE(file);
1018 level->gems_needed = getFile16BitBE(file);
1020 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1021 level->name[i] = getFile8Bit(file);
1022 level->name[MAX_LEVEL_NAME_LEN] = 0;
1024 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1025 level->score[i] = getFile8Bit(file);
1027 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1028 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1029 for (y = 0; y < 3; y++)
1030 for (x = 0; x < 3; x++)
1031 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
1033 level->amoeba_speed = getFile8Bit(file);
1034 level->time_magic_wall = getFile8Bit(file);
1035 level->time_wheel = getFile8Bit(file);
1036 level->amoeba_content = getMappedElement(getFile8Bit(file));
1037 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1038 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1039 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1040 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1042 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1044 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1045 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1046 level->can_move_into_acid_bits = getFile32BitBE(file);
1047 level->dont_collide_with_bits = getFile8Bit(file);
1049 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1050 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1052 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1053 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1054 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1056 level->game_engine_type = getFile8Bit(file);
1058 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
1063 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
1067 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1068 level->author[i] = getFile8Bit(file);
1069 level->author[MAX_LEVEL_NAME_LEN] = 0;
1074 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
1077 int chunk_size_expected = level->fieldx * level->fieldy;
1079 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1080 stored with 16-bit encoding (and should be twice as big then).
1081 Even worse, playfield data was stored 16-bit when only yamyam content
1082 contained 16-bit elements and vice versa. */
1084 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1085 chunk_size_expected *= 2;
1087 if (chunk_size_expected != chunk_size)
1089 ReadUnusedBytesFromFile(file, chunk_size);
1090 return chunk_size_expected;
1093 for (y = 0; y < level->fieldy; y++)
1094 for (x = 0; x < level->fieldx; x++)
1095 level->field[x][y] =
1096 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
1101 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
1104 int header_size = 4;
1105 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
1106 int chunk_size_expected = header_size + content_size;
1108 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1109 stored with 16-bit encoding (and should be twice as big then).
1110 Even worse, playfield data was stored 16-bit when only yamyam content
1111 contained 16-bit elements and vice versa. */
1113 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1114 chunk_size_expected += content_size;
1116 if (chunk_size_expected != chunk_size)
1118 ReadUnusedBytesFromFile(file, chunk_size);
1119 return chunk_size_expected;
1123 level->num_yamyam_contents = getFile8Bit(file);
1127 /* correct invalid number of content fields -- should never happen */
1128 if (level->num_yamyam_contents < 1 ||
1129 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
1130 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1132 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1133 for (y = 0; y < 3; y++)
1134 for (x = 0; x < 3; x++)
1135 level->yamyam_content[i].e[x][y] =
1136 getMappedElement(level->encoding_16bit_field ?
1137 getFile16BitBE(file) : getFile8Bit(file));
1141 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
1145 int num_contents, content_xsize, content_ysize;
1146 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1148 element = getMappedElement(getFile16BitBE(file));
1149 num_contents = getFile8Bit(file);
1150 content_xsize = getFile8Bit(file);
1151 content_ysize = getFile8Bit(file);
1153 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1155 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1156 for (y = 0; y < 3; y++)
1157 for (x = 0; x < 3; x++)
1158 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
1160 /* correct invalid number of content fields -- should never happen */
1161 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
1162 num_contents = STD_ELEMENT_CONTENTS;
1164 if (element == EL_YAMYAM)
1166 level->num_yamyam_contents = num_contents;
1168 for (i = 0; i < num_contents; i++)
1169 for (y = 0; y < 3; y++)
1170 for (x = 0; x < 3; x++)
1171 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
1173 else if (element == EL_BD_AMOEBA)
1175 level->amoeba_content = content_array[0][0][0];
1179 Error(ERR_WARN, "cannot load content for element '%d'", element);
1185 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
1191 int chunk_size_expected;
1193 element = getMappedElement(getFile16BitBE(file));
1194 if (!IS_ENVELOPE(element))
1195 element = EL_ENVELOPE_1;
1197 envelope_nr = element - EL_ENVELOPE_1;
1199 envelope_len = getFile16BitBE(file);
1201 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
1202 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
1204 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1206 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
1207 if (chunk_size_expected != chunk_size)
1209 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
1210 return chunk_size_expected;
1213 for (i = 0; i < envelope_len; i++)
1214 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
1219 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
1221 int num_changed_custom_elements = getFile16BitBE(file);
1222 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
1225 if (chunk_size_expected != chunk_size)
1227 ReadUnusedBytesFromFile(file, chunk_size - 2);
1228 return chunk_size_expected;
1231 for (i = 0; i < num_changed_custom_elements; i++)
1233 int element = getFile16BitBE(file);
1234 int properties = getFile32BitBE(file);
1236 if (IS_CUSTOM_ELEMENT(element))
1237 Properties[element][EP_BITFIELD_BASE] = properties;
1239 Error(ERR_WARN, "invalid custom element number %d", element);
1245 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
1247 int num_changed_custom_elements = getFile16BitBE(file);
1248 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
1251 if (chunk_size_expected != chunk_size)
1253 ReadUnusedBytesFromFile(file, chunk_size - 2);
1254 return chunk_size_expected;
1257 for (i = 0; i < num_changed_custom_elements; i++)
1259 int element = getFile16BitBE(file);
1260 int custom_target_element = getFile16BitBE(file);
1262 if (IS_CUSTOM_ELEMENT(element))
1263 element_info[element].change->target_element = custom_target_element;
1265 Error(ERR_WARN, "invalid custom element number %d", element);
1271 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
1273 int num_changed_custom_elements = getFile16BitBE(file);
1274 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1277 if (chunk_size_expected != chunk_size)
1279 ReadUnusedBytesFromFile(file, chunk_size - 2);
1280 return chunk_size_expected;
1283 for (i = 0; i < num_changed_custom_elements; i++)
1285 int element = getFile16BitBE(file);
1286 struct ElementInfo *ei = &element_info[element];
1287 unsigned long event_bits;
1289 if (!IS_CUSTOM_ELEMENT(element))
1291 Error(ERR_WARN, "invalid custom element number %d", element);
1293 element = EL_INTERNAL_DUMMY;
1296 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1297 ei->description[j] = getFile8Bit(file);
1298 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1300 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1302 /* some free bytes for future properties and padding */
1303 ReadUnusedBytesFromFile(file, 7);
1305 ei->use_gfx_element = getFile8Bit(file);
1306 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1308 ei->collect_score_initial = getFile8Bit(file);
1309 ei->collect_count_initial = getFile8Bit(file);
1311 ei->push_delay_fixed = getFile16BitBE(file);
1312 ei->push_delay_random = getFile16BitBE(file);
1313 ei->move_delay_fixed = getFile16BitBE(file);
1314 ei->move_delay_random = getFile16BitBE(file);
1316 ei->move_pattern = getFile16BitBE(file);
1317 ei->move_direction_initial = getFile8Bit(file);
1318 ei->move_stepsize = getFile8Bit(file);
1320 for (y = 0; y < 3; y++)
1321 for (x = 0; x < 3; x++)
1322 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1324 event_bits = getFile32BitBE(file);
1325 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1326 if (event_bits & (1 << j))
1327 ei->change->has_event[j] = TRUE;
1329 ei->change->target_element = getMappedElement(getFile16BitBE(file));
1331 ei->change->delay_fixed = getFile16BitBE(file);
1332 ei->change->delay_random = getFile16BitBE(file);
1333 ei->change->delay_frames = getFile16BitBE(file);
1335 ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
1337 ei->change->explode = getFile8Bit(file);
1338 ei->change->use_target_content = getFile8Bit(file);
1339 ei->change->only_if_complete = getFile8Bit(file);
1340 ei->change->use_random_replace = getFile8Bit(file);
1342 ei->change->random_percentage = getFile8Bit(file);
1343 ei->change->replace_when = getFile8Bit(file);
1345 for (y = 0; y < 3; y++)
1346 for (x = 0; x < 3; x++)
1347 ei->change->target_content.e[x][y] =
1348 getMappedElement(getFile16BitBE(file));
1350 ei->slippery_type = getFile8Bit(file);
1352 /* some free bytes for future properties and padding */
1353 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1355 /* mark that this custom element has been modified */
1356 ei->modified_settings = TRUE;
1362 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1364 struct ElementInfo *ei;
1365 int chunk_size_expected;
1369 /* ---------- custom element base property values (96 bytes) ------------- */
1371 element = getFile16BitBE(file);
1373 if (!IS_CUSTOM_ELEMENT(element))
1375 Error(ERR_WARN, "invalid custom element number %d", element);
1377 ReadUnusedBytesFromFile(file, chunk_size - 2);
1381 ei = &element_info[element];
1383 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1384 ei->description[i] = getFile8Bit(file);
1385 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1387 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1388 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1390 ei->num_change_pages = getFile8Bit(file);
1392 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1393 if (chunk_size_expected != chunk_size)
1395 ReadUnusedBytesFromFile(file, chunk_size - 43);
1396 return chunk_size_expected;
1399 ei->ce_value_fixed_initial = getFile16BitBE(file);
1400 ei->ce_value_random_initial = getFile16BitBE(file);
1401 ei->use_last_ce_value = getFile8Bit(file);
1403 ei->use_gfx_element = getFile8Bit(file);
1404 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1406 ei->collect_score_initial = getFile8Bit(file);
1407 ei->collect_count_initial = getFile8Bit(file);
1409 ei->drop_delay_fixed = getFile8Bit(file);
1410 ei->push_delay_fixed = getFile8Bit(file);
1411 ei->drop_delay_random = getFile8Bit(file);
1412 ei->push_delay_random = getFile8Bit(file);
1413 ei->move_delay_fixed = getFile16BitBE(file);
1414 ei->move_delay_random = getFile16BitBE(file);
1416 /* bits 0 - 15 of "move_pattern" ... */
1417 ei->move_pattern = getFile16BitBE(file);
1418 ei->move_direction_initial = getFile8Bit(file);
1419 ei->move_stepsize = getFile8Bit(file);
1421 ei->slippery_type = getFile8Bit(file);
1423 for (y = 0; y < 3; y++)
1424 for (x = 0; x < 3; x++)
1425 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1427 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1428 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1429 ei->move_leave_type = getFile8Bit(file);
1431 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1432 ei->move_pattern |= (getFile16BitBE(file) << 16);
1434 ei->access_direction = getFile8Bit(file);
1436 ei->explosion_delay = getFile8Bit(file);
1437 ei->ignition_delay = getFile8Bit(file);
1438 ei->explosion_type = getFile8Bit(file);
1440 /* some free bytes for future custom property values and padding */
1441 ReadUnusedBytesFromFile(file, 1);
1443 /* ---------- change page property values (48 bytes) --------------------- */
1445 setElementChangePages(ei, ei->num_change_pages);
1447 for (i = 0; i < ei->num_change_pages; i++)
1449 struct ElementChangeInfo *change = &ei->change_page[i];
1450 unsigned long event_bits;
1452 /* always start with reliable default values */
1453 setElementChangeInfoToDefaults(change);
1455 event_bits = getFile32BitBE(file);
1456 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1457 if (event_bits & (1 << j))
1458 change->has_event[j] = TRUE;
1460 change->target_element = getMappedElement(getFile16BitBE(file));
1462 change->delay_fixed = getFile16BitBE(file);
1463 change->delay_random = getFile16BitBE(file);
1464 change->delay_frames = getFile16BitBE(file);
1466 change->trigger_element = getMappedElement(getFile16BitBE(file));
1468 change->explode = getFile8Bit(file);
1469 change->use_target_content = getFile8Bit(file);
1470 change->only_if_complete = getFile8Bit(file);
1471 change->use_random_replace = getFile8Bit(file);
1473 change->random_percentage = getFile8Bit(file);
1474 change->replace_when = getFile8Bit(file);
1476 for (y = 0; y < 3; y++)
1477 for (x = 0; x < 3; x++)
1478 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
1480 change->can_change = getFile8Bit(file);
1482 change->trigger_side = getFile8Bit(file);
1484 change->trigger_player = getFile8Bit(file);
1485 change->trigger_page = getFile8Bit(file);
1487 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1488 CH_PAGE_ANY : (1 << change->trigger_page));
1490 change->has_action = getFile8Bit(file);
1491 change->action_type = getFile8Bit(file);
1492 change->action_mode = getFile8Bit(file);
1493 change->action_arg = getFile16BitBE(file);
1495 /* some free bytes for future change property values and padding */
1496 ReadUnusedBytesFromFile(file, 1);
1499 /* mark this custom element as modified */
1500 ei->modified_settings = TRUE;
1505 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1507 struct ElementInfo *ei;
1508 struct ElementGroupInfo *group;
1512 element = getFile16BitBE(file);
1514 if (!IS_GROUP_ELEMENT(element))
1516 Error(ERR_WARN, "invalid group element number %d", element);
1518 ReadUnusedBytesFromFile(file, chunk_size - 2);
1522 ei = &element_info[element];
1524 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1525 ei->description[i] = getFile8Bit(file);
1526 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1528 group = element_info[element].group;
1530 group->num_elements = getFile8Bit(file);
1532 ei->use_gfx_element = getFile8Bit(file);
1533 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1535 group->choice_mode = getFile8Bit(file);
1537 /* some free bytes for future values and padding */
1538 ReadUnusedBytesFromFile(file, 3);
1540 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1541 group->element[i] = getMappedElement(getFile16BitBE(file));
1543 /* mark this group element as modified */
1544 element_info[element].modified_settings = TRUE;
1549 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
1551 int real_chunk_size = 0;
1556 int element = getFile16BitBE(file);
1557 int type = getFile8Bit(file);
1558 int bytes = type & CONF_MASK_BYTES;
1559 boolean element_found = FALSE;
1561 real_chunk_size += 3;
1563 li = *level; /* copy level information into temporary buffer */
1565 if (bytes == CONF_MASK_MULTI_BYTES)
1567 int num_bytes = getFile16BitBE(file);
1568 byte *buffer = checked_malloc(num_bytes);
1570 ReadBytesFromFile(file, buffer, num_bytes);
1572 for (i = 0; element_conf[i].element != -1; i++)
1574 if (element_conf[i].element == element &&
1575 element_conf[i].type == type)
1577 element_found = TRUE;
1579 if (type == CONF_VALUE_CONTENT_8)
1581 struct Content *content= (struct Content *)(element_conf[i].value);
1582 int num_contents = num_bytes / CONF_CONTENT_NUM_BYTES;
1585 for (c = 0; c < num_contents; c++)
1586 for (y = 0; y < 3; y++)
1587 for (x = 0; x < 3; x++)
1588 content[c].e[x][y] =
1589 getMappedElement(CONF_CONTENT_ELEMENT(buffer, c, x, y));
1592 element_found = FALSE;
1598 checked_free(buffer);
1600 real_chunk_size += 2 + num_bytes;
1604 int value = (bytes == CONF_MASK_1_BYTE ? getFile8Bit (file) :
1605 bytes == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
1606 bytes == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
1608 for (i = 0; element_conf[i].element != -1; i++)
1610 if (element_conf[i].element == element &&
1611 element_conf[i].type == type)
1613 if (CONF_VALUE_BOOLEAN(type))
1614 *(boolean *)(element_conf[i].value) = value;
1616 *(int *) (element_conf[i].value) = value;
1618 element_found = TRUE;
1624 real_chunk_size += CONF_VALUE_NUM_BYTES(bytes);
1627 *level = li; /* copy temporary buffer back to level information */
1630 Error(ERR_WARN, "cannot load CONF value for element %d", element);
1632 if (type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
1636 return real_chunk_size;
1639 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1640 struct LevelFileInfo *level_file_info)
1642 char *filename = level_file_info->filename;
1643 char cookie[MAX_LINE_LEN];
1644 char chunk_name[CHUNK_ID_LEN + 1];
1648 if (!(file = fopen(filename, MODE_READ)))
1650 level->no_valid_file = TRUE;
1652 if (level != &level_template)
1653 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1658 getFileChunkBE(file, chunk_name, NULL);
1659 if (strcmp(chunk_name, "RND1") == 0)
1661 getFile32BitBE(file); /* not used */
1663 getFileChunkBE(file, chunk_name, NULL);
1664 if (strcmp(chunk_name, "CAVE") != 0)
1666 level->no_valid_file = TRUE;
1668 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1673 else /* check for pre-2.0 file format with cookie string */
1675 strcpy(cookie, chunk_name);
1676 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1677 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1678 cookie[strlen(cookie) - 1] = '\0';
1680 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1682 level->no_valid_file = TRUE;
1684 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1689 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1691 level->no_valid_file = TRUE;
1693 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1698 /* pre-2.0 level files have no game version, so use file version here */
1699 level->game_version = level->file_version;
1702 if (level->file_version < FILE_VERSION_1_2)
1704 /* level files from versions before 1.2.0 without chunk structure */
1705 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1706 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1714 int (*loader)(FILE *, int, struct LevelInfo *);
1718 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1719 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1720 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1721 { "BODY", -1, LoadLevel_BODY },
1722 { "CONT", -1, LoadLevel_CONT },
1723 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1724 { "CNT3", -1, LoadLevel_CNT3 },
1725 { "CUS1", -1, LoadLevel_CUS1 },
1726 { "CUS2", -1, LoadLevel_CUS2 },
1727 { "CUS3", -1, LoadLevel_CUS3 },
1728 { "CUS4", -1, LoadLevel_CUS4 },
1729 { "GRP1", -1, LoadLevel_GRP1 },
1730 { "CONF", -1, LoadLevel_CONF },
1735 while (getFileChunkBE(file, chunk_name, &chunk_size))
1739 while (chunk_info[i].name != NULL &&
1740 strcmp(chunk_name, chunk_info[i].name) != 0)
1743 if (chunk_info[i].name == NULL)
1745 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1746 chunk_name, filename);
1747 ReadUnusedBytesFromFile(file, chunk_size);
1749 else if (chunk_info[i].size != -1 &&
1750 chunk_info[i].size != chunk_size)
1752 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1753 chunk_size, chunk_name, filename);
1754 ReadUnusedBytesFromFile(file, chunk_size);
1758 /* call function to load this level chunk */
1759 int chunk_size_expected =
1760 (chunk_info[i].loader)(file, chunk_size, level);
1762 /* the size of some chunks cannot be checked before reading other
1763 chunks first (like "HEAD" and "BODY") that contain some header
1764 information, so check them here */
1765 if (chunk_size_expected != chunk_size)
1767 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1768 chunk_size, chunk_name, filename);
1777 /* ------------------------------------------------------------------------- */
1778 /* functions for loading EM level */
1779 /* ------------------------------------------------------------------------- */
1783 static int map_em_element_yam(int element)
1787 case 0x00: return EL_EMPTY;
1788 case 0x01: return EL_EMERALD;
1789 case 0x02: return EL_DIAMOND;
1790 case 0x03: return EL_ROCK;
1791 case 0x04: return EL_ROBOT;
1792 case 0x05: return EL_SPACESHIP_UP;
1793 case 0x06: return EL_BOMB;
1794 case 0x07: return EL_BUG_UP;
1795 case 0x08: return EL_AMOEBA_DROP;
1796 case 0x09: return EL_NUT;
1797 case 0x0a: return EL_YAMYAM;
1798 case 0x0b: return EL_QUICKSAND_FULL;
1799 case 0x0c: return EL_SAND;
1800 case 0x0d: return EL_WALL_SLIPPERY;
1801 case 0x0e: return EL_STEELWALL;
1802 case 0x0f: return EL_WALL;
1803 case 0x10: return EL_EM_KEY_1;
1804 case 0x11: return EL_EM_KEY_2;
1805 case 0x12: return EL_EM_KEY_4;
1806 case 0x13: return EL_EM_KEY_3;
1807 case 0x14: return EL_MAGIC_WALL;
1808 case 0x15: return EL_ROBOT_WHEEL;
1809 case 0x16: return EL_DYNAMITE;
1811 case 0x17: return EL_EM_KEY_1; /* EMC */
1812 case 0x18: return EL_BUG_UP; /* EMC */
1813 case 0x1a: return EL_DIAMOND; /* EMC */
1814 case 0x1b: return EL_EMERALD; /* EMC */
1815 case 0x25: return EL_NUT; /* EMC */
1816 case 0x80: return EL_EMPTY; /* EMC */
1817 case 0x85: return EL_EM_KEY_1; /* EMC */
1818 case 0x86: return EL_EM_KEY_2; /* EMC */
1819 case 0x87: return EL_EM_KEY_4; /* EMC */
1820 case 0x88: return EL_EM_KEY_3; /* EMC */
1821 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1822 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1823 case 0xaf: return EL_DYNAMITE; /* EMC */
1824 case 0xbd: return EL_SAND; /* EMC */
1827 Error(ERR_WARN, "invalid level element %d", element);
1832 static int map_em_element_field(int element)
1834 if (element >= 0xc8 && element <= 0xe1)
1835 return EL_CHAR_A + (element - 0xc8);
1836 else if (element >= 0xe2 && element <= 0xeb)
1837 return EL_CHAR_0 + (element - 0xe2);
1841 case 0x00: return EL_ROCK;
1842 case 0x01: return EL_ROCK; /* EMC */
1843 case 0x02: return EL_DIAMOND;
1844 case 0x03: return EL_DIAMOND;
1845 case 0x04: return EL_ROBOT;
1846 case 0x05: return EL_ROBOT; /* EMC */
1847 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1848 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1849 case 0x08: return EL_SPACESHIP_UP;
1850 case 0x09: return EL_SPACESHIP_RIGHT;
1851 case 0x0a: return EL_SPACESHIP_DOWN;
1852 case 0x0b: return EL_SPACESHIP_LEFT;
1853 case 0x0c: return EL_SPACESHIP_UP;
1854 case 0x0d: return EL_SPACESHIP_RIGHT;
1855 case 0x0e: return EL_SPACESHIP_DOWN;
1856 case 0x0f: return EL_SPACESHIP_LEFT;
1858 case 0x10: return EL_BOMB;
1859 case 0x11: return EL_BOMB; /* EMC */
1860 case 0x12: return EL_EMERALD;
1861 case 0x13: return EL_EMERALD;
1862 case 0x14: return EL_BUG_UP;
1863 case 0x15: return EL_BUG_RIGHT;
1864 case 0x16: return EL_BUG_DOWN;
1865 case 0x17: return EL_BUG_LEFT;
1866 case 0x18: return EL_BUG_UP;
1867 case 0x19: return EL_BUG_RIGHT;
1868 case 0x1a: return EL_BUG_DOWN;
1869 case 0x1b: return EL_BUG_LEFT;
1870 case 0x1c: return EL_AMOEBA_DROP;
1871 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1872 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1873 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1875 case 0x20: return EL_ROCK;
1876 case 0x21: return EL_BOMB; /* EMC */
1877 case 0x22: return EL_DIAMOND; /* EMC */
1878 case 0x23: return EL_EMERALD; /* EMC */
1879 case 0x24: return EL_MAGIC_WALL;
1880 case 0x25: return EL_NUT;
1881 case 0x26: return EL_NUT; /* EMC */
1882 case 0x27: return EL_NUT; /* EMC */
1884 /* looks like magic wheel, but is _always_ activated */
1885 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1887 case 0x29: return EL_YAMYAM; /* up */
1888 case 0x2a: return EL_YAMYAM; /* down */
1889 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1890 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1891 case 0x2d: return EL_QUICKSAND_FULL;
1892 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1893 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1895 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1896 case 0x31: return EL_SAND; /* EMC */
1897 case 0x32: return EL_SAND; /* EMC */
1898 case 0x33: return EL_SAND; /* EMC */
1899 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1900 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1901 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1902 case 0x37: return EL_SAND; /* EMC */
1903 case 0x38: return EL_ROCK; /* EMC */
1904 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1905 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1906 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1907 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1908 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1909 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1910 case 0x3f: return EL_ACID_POOL_BOTTOM;
1912 case 0x40: return EL_EXIT_OPEN; /* 1 */
1913 case 0x41: return EL_EXIT_OPEN; /* 2 */
1914 case 0x42: return EL_EXIT_OPEN; /* 3 */
1915 case 0x43: return EL_BALLOON; /* EMC */
1916 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1917 case 0x45: return EL_SPRING; /* EMC */
1918 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1919 case 0x47: return EL_SPRING; /* left */ /* EMC */
1920 case 0x48: return EL_SPRING; /* right */ /* EMC */
1921 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1922 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1923 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1924 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1925 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1926 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1927 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1929 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1930 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1931 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1932 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1933 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1934 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1935 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1936 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1937 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1938 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1939 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1940 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1941 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1942 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1943 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1944 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1946 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1947 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1948 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1949 case 0x63: return EL_SPRING; /* left */ /* EMC */
1950 case 0x64: return EL_SPRING; /* right */ /* EMC */
1951 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1952 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1953 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1954 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1955 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1956 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1957 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1958 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1959 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1960 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1961 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1963 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1964 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1965 case 0x72: return EL_NUT; /* left */ /* EMC */
1966 case 0x73: return EL_SAND; /* EMC (? "nut") */
1967 case 0x74: return EL_STEELWALL;
1968 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1969 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1970 case 0x77: return EL_BOMB; /* left */ /* EMC */
1971 case 0x78: return EL_BOMB; /* right */ /* EMC */
1972 case 0x79: return EL_ROCK; /* left */ /* EMC */
1973 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1974 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1975 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1976 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1977 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1978 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1980 case 0x80: return EL_EMPTY;
1981 case 0x81: return EL_WALL_SLIPPERY;
1982 case 0x82: return EL_SAND;
1983 case 0x83: return EL_STEELWALL;
1984 case 0x84: return EL_WALL;
1985 case 0x85: return EL_EM_KEY_1;
1986 case 0x86: return EL_EM_KEY_2;
1987 case 0x87: return EL_EM_KEY_4;
1988 case 0x88: return EL_EM_KEY_3;
1989 case 0x89: return EL_EM_GATE_1;
1990 case 0x8a: return EL_EM_GATE_2;
1991 case 0x8b: return EL_EM_GATE_4;
1992 case 0x8c: return EL_EM_GATE_3;
1993 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1994 case 0x8e: return EL_EM_GATE_1_GRAY;
1995 case 0x8f: return EL_EM_GATE_2_GRAY;
1997 case 0x90: return EL_EM_GATE_4_GRAY;
1998 case 0x91: return EL_EM_GATE_3_GRAY;
1999 case 0x92: return EL_MAGIC_WALL;
2000 case 0x93: return EL_ROBOT_WHEEL;
2001 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
2002 case 0x95: return EL_ACID_POOL_TOPLEFT;
2003 case 0x96: return EL_ACID_POOL_TOPRIGHT;
2004 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
2005 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
2006 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
2007 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
2008 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
2009 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
2010 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
2011 case 0x9e: return EL_EXIT_CLOSED;
2012 case 0x9f: return EL_CHAR_LESS; /* arrow left */
2014 /* looks like normal sand, but behaves like wall */
2015 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
2016 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
2017 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
2018 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
2019 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
2020 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
2021 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
2022 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
2023 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
2024 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
2025 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
2026 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
2027 case 0xac: return EL_CHAR_COMMA; /* EMC */
2028 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
2029 case 0xae: return EL_CHAR_MINUS; /* EMC */
2030 case 0xaf: return EL_DYNAMITE;
2032 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
2033 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
2034 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
2035 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
2036 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
2037 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
2038 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
2039 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
2040 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
2041 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
2042 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
2043 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
2044 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
2045 case 0xbd: return EL_SAND; /* EMC ("dirt") */
2046 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
2047 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
2049 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
2050 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
2051 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
2052 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
2053 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
2054 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
2055 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
2056 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
2058 /* characters: see above */
2060 case 0xec: return EL_CHAR_PERIOD;
2061 case 0xed: return EL_CHAR_EXCLAM;
2062 case 0xee: return EL_CHAR_COLON;
2063 case 0xef: return EL_CHAR_QUESTION;
2065 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
2066 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
2067 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
2068 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
2069 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
2070 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
2071 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
2072 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
2074 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
2075 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
2076 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
2077 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
2078 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
2079 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
2081 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
2082 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
2085 /* should never happen (all 8-bit value cases should be handled) */
2086 Error(ERR_WARN, "invalid level element %d", element);
2091 #define EM_LEVEL_SIZE 2106
2092 #define EM_LEVEL_XSIZE 64
2093 #define EM_LEVEL_YSIZE 32
2095 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2096 struct LevelFileInfo *level_file_info)
2098 char *filename = level_file_info->filename;
2100 unsigned char leveldata[EM_LEVEL_SIZE];
2101 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
2102 int nr = level_file_info->nr;
2105 if (!(file = fopen(filename, MODE_READ)))
2107 level->no_valid_file = TRUE;
2109 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2114 for (i = 0; i < EM_LEVEL_SIZE; i++)
2115 leveldata[i] = fgetc(file);
2119 /* check if level data is crypted by testing against known starting bytes
2120 of the few existing crypted level files (from Emerald Mine 1 + 2) */
2122 if ((leveldata[0] == 0xf1 ||
2123 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
2125 unsigned char code0 = 0x65;
2126 unsigned char code1 = 0x11;
2128 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
2129 leveldata[0] = 0xf1;
2131 /* decode crypted level data */
2133 for (i = 0; i < EM_LEVEL_SIZE; i++)
2135 leveldata[i] ^= code0;
2136 leveldata[i] -= code1;
2138 code0 = (code0 + 7) & 0xff;
2142 level->fieldx = EM_LEVEL_XSIZE;
2143 level->fieldy = EM_LEVEL_YSIZE;
2145 level->time = header[46] * 10;
2146 level->gems_needed = header[47];
2148 /* The original Emerald Mine levels have their level number stored
2149 at the second byte of the level file...
2150 Do not trust this information at other level files, e.g. EMC,
2151 but correct it anyway (normally the first row is completely
2152 steel wall, so the correction does not hurt anyway). */
2154 if (leveldata[1] == nr)
2155 leveldata[1] = leveldata[2]; /* correct level number field */
2157 sprintf(level->name, "Level %d", nr); /* set level name */
2159 level->score[SC_EMERALD] = header[36];
2160 level->score[SC_DIAMOND] = header[37];
2161 level->score[SC_ROBOT] = header[38];
2162 level->score[SC_SPACESHIP] = header[39];
2163 level->score[SC_BUG] = header[40];
2164 level->score[SC_YAMYAM] = header[41];
2165 level->score[SC_NUT] = header[42];
2166 level->score[SC_DYNAMITE] = header[43];
2167 level->score[SC_TIME_BONUS] = header[44];
2169 level->num_yamyam_contents = 4;
2171 for (i = 0; i < level->num_yamyam_contents; i++)
2172 for (y = 0; y < 3; y++)
2173 for (x = 0; x < 3; x++)
2174 level->yamyam_content[i].e[x][y] =
2175 map_em_element_yam(header[i * 9 + y * 3 + x]);
2177 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
2178 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
2179 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
2180 level->amoeba_content = EL_DIAMOND;
2182 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2184 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
2186 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
2187 new_element = EL_AMOEBA_WET;
2189 level->field[x][y] = new_element;
2192 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
2193 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
2194 level->field[x][y] = EL_PLAYER_1;
2196 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
2197 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
2198 level->field[x][y] = EL_PLAYER_2;
2203 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
2205 static int ball_xy[8][2] =
2216 struct LevelInfo_EM *level_em = level->native_em_level;
2217 struct LEVEL *lev = level_em->lev;
2218 struct PLAYER *ply1 = level_em->ply1;
2219 struct PLAYER *ply2 = level_em->ply2;
2222 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
2223 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
2225 lev->time_seconds = level->time;
2226 lev->required_initial = level->gems_needed;
2228 lev->emerald_score = level->score[SC_EMERALD];
2229 lev->diamond_score = level->score[SC_DIAMOND];
2230 lev->alien_score = level->score[SC_ROBOT];
2231 lev->tank_score = level->score[SC_SPACESHIP];
2232 lev->bug_score = level->score[SC_BUG];
2233 lev->eater_score = level->score[SC_YAMYAM];
2234 lev->nut_score = level->score[SC_NUT];
2235 lev->dynamite_score = level->score[SC_DYNAMITE];
2236 lev->key_score = level->score[SC_KEY];
2237 lev->exit_score = level->score[SC_TIME_BONUS];
2239 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2240 for (y = 0; y < 3; y++)
2241 for (x = 0; x < 3; x++)
2242 lev->eater_array[i][y * 3 + x] =
2243 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
2245 lev->amoeba_time = level->amoeba_speed;
2246 lev->wonderwall_time_initial = level->time_magic_wall;
2247 lev->wheel_time = level->time_wheel;
2249 lev->android_move_time = level->android_move_time;
2250 lev->android_clone_time = level->android_clone_time;
2251 lev->ball_random = level->ball_random;
2252 lev->ball_state_initial = level->ball_state_initial;
2253 lev->ball_time = level->ball_time;
2255 lev->lenses_score = level->lenses_score;
2256 lev->magnify_score = level->magnify_score;
2257 lev->slurp_score = level->slurp_score;
2259 lev->lenses_time = level->lenses_time;
2260 lev->magnify_time = level->magnify_time;
2261 lev->wind_direction_initial = level->wind_direction_initial;
2263 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2264 for (j = 0; j < 8; j++)
2265 lev->ball_array[i][j] =
2266 map_element_RND_to_EM(level->
2267 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
2269 for (i = 0; i < 16; i++)
2270 lev->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2272 /* first fill the complete playfield with the default border element */
2273 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
2274 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
2275 level_em->cave[x][y] = ZBORDER;
2277 /* then copy the real level contents from level file into the playfield */
2278 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2280 int new_element = map_element_RND_to_EM(level->field[x][y]);
2282 if (level->field[x][y] == EL_AMOEBA_DEAD)
2283 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
2285 level_em->cave[x + 1][y + 1] = new_element;
2288 ply1->x_initial = 0;
2289 ply1->y_initial = 0;
2291 ply2->x_initial = 0;
2292 ply2->y_initial = 0;
2294 /* initialize player positions and delete players from the playfield */
2295 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2297 if (level->field[x][y] == EL_PLAYER_1)
2299 ply1->x_initial = x + 1;
2300 ply1->y_initial = y + 1;
2301 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2303 else if (level->field[x][y] == EL_PLAYER_2)
2305 ply2->x_initial = x + 1;
2306 ply2->y_initial = y + 1;
2307 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
2312 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
2314 static int ball_xy[8][2] =
2325 struct LevelInfo_EM *level_em = level->native_em_level;
2326 struct LEVEL *lev = level_em->lev;
2327 struct PLAYER *ply1 = level_em->ply1;
2328 struct PLAYER *ply2 = level_em->ply2;
2331 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
2332 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
2334 level->time = lev->time_seconds;
2335 level->gems_needed = lev->required_initial;
2337 sprintf(level->name, "Level %d", level->file_info.nr);
2339 level->score[SC_EMERALD] = lev->emerald_score;
2340 level->score[SC_DIAMOND] = lev->diamond_score;
2341 level->score[SC_ROBOT] = lev->alien_score;
2342 level->score[SC_SPACESHIP] = lev->tank_score;
2343 level->score[SC_BUG] = lev->bug_score;
2344 level->score[SC_YAMYAM] = lev->eater_score;
2345 level->score[SC_NUT] = lev->nut_score;
2346 level->score[SC_DYNAMITE] = lev->dynamite_score;
2347 level->score[SC_KEY] = lev->key_score;
2348 level->score[SC_TIME_BONUS] = lev->exit_score;
2350 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
2352 for (i = 0; i < level->num_yamyam_contents; i++)
2353 for (y = 0; y < 3; y++)
2354 for (x = 0; x < 3; x++)
2355 level->yamyam_content[i].e[x][y] =
2356 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
2358 level->amoeba_speed = lev->amoeba_time;
2359 level->time_magic_wall = lev->wonderwall_time_initial;
2360 level->time_wheel = lev->wheel_time;
2362 level->android_move_time = lev->android_move_time;
2363 level->android_clone_time = lev->android_clone_time;
2364 level->ball_random = lev->ball_random;
2365 level->ball_state_initial = lev->ball_state_initial;
2366 level->ball_time = lev->ball_time;
2368 level->lenses_score = lev->lenses_score;
2369 level->magnify_score = lev->magnify_score;
2370 level->slurp_score = lev->slurp_score;
2372 level->lenses_time = lev->lenses_time;
2373 level->magnify_time = lev->magnify_time;
2374 level->wind_direction_initial = lev->wind_direction_initial;
2376 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2377 for (j = 0; j < 8; j++)
2378 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
2379 map_element_EM_to_RND(lev->ball_array[i][j]);
2381 for (i = 0; i < 16; i++)
2382 level->android_array[i] = FALSE; /* !!! YET TO COME !!! */
2384 /* convert the playfield (some elements need special treatment) */
2385 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2387 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
2389 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
2390 new_element = EL_AMOEBA_DEAD;
2392 level->field[x][y] = new_element;
2395 /* in case of both players set to the same field, use the first player */
2396 level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
2397 level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
2400 printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
2404 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2405 struct LevelFileInfo *level_file_info)
2407 if (!LoadNativeLevel_EM(level_file_info->filename))
2408 level->no_valid_file = TRUE;
2411 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
2413 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2414 CopyNativeLevel_RND_to_EM(level);
2417 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
2419 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2420 CopyNativeLevel_EM_to_RND(level);
2424 /* ------------------------------------------------------------------------- */
2425 /* functions for loading SP level */
2426 /* ------------------------------------------------------------------------- */
2428 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2429 #define SP_LEVEL_SIZE 1536
2430 #define SP_LEVEL_XSIZE 60
2431 #define SP_LEVEL_YSIZE 24
2432 #define SP_LEVEL_NAME_LEN 23
2434 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2437 int num_special_ports;
2440 /* for details of the Supaplex level format, see Herman Perk's Supaplex
2441 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2443 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2444 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2446 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2448 int element_old = fgetc(file);
2451 if (element_old <= 0x27)
2452 element_new = getMappedElement(EL_SP_START + element_old);
2453 else if (element_old == 0x28)
2454 element_new = EL_INVISIBLE_WALL;
2457 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2458 Error(ERR_WARN, "invalid level element %d", element_old);
2460 element_new = EL_UNKNOWN;
2463 level->field[x][y] = element_new;
2467 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2469 /* initial gravity: 1 == "on", anything else (0) == "off" */
2470 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2472 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2474 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2475 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2476 level->name[i] = fgetc(file);
2477 level->name[SP_LEVEL_NAME_LEN] = '\0';
2479 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2480 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2482 /* number of infotrons needed; 0 means that Supaplex will count the total
2483 amount of infotrons in the level and use the low byte of that number
2484 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2485 level->gems_needed = fgetc(file);
2487 /* number of special ("gravity") port entries below (maximum 10 allowed) */
2488 num_special_ports = fgetc(file);
2490 /* database of properties of up to 10 special ports (6 bytes per port) */
2491 for (i = 0; i < 10; i++)
2493 int port_location, port_x, port_y, port_element;
2496 /* high and low byte of the location of a special port; if (x, y) are the
2497 coordinates of a port in the field and (0, 0) is the top-left corner,
2498 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2499 of what may be expected: Supaplex works with a game field in memory
2500 which is 2 bytes per tile) */
2501 port_location = getFile16BitBE(file);
2503 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2504 gravity = fgetc(file);
2506 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2507 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2509 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2510 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
2512 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
2514 if (i >= num_special_ports)
2517 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2518 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2520 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2521 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2523 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2529 port_element = level->field[port_x][port_y];
2531 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2532 port_element > EL_SP_GRAVITY_PORT_UP)
2534 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2539 /* change previous (wrong) gravity inverting special port to either
2540 gravity enabling special port or gravity disabling special port */
2541 level->field[port_x][port_y] +=
2542 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2543 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2546 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
2548 /* change special gravity ports without database entries to normal ports */
2549 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2550 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2551 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2552 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2553 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2555 /* auto-determine number of infotrons if it was stored as "0" -- see above */
2556 if (level->gems_needed == 0)
2558 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2559 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2560 if (level->field[x][y] == EL_SP_INFOTRON)
2561 level->gems_needed++;
2563 level->gems_needed &= 0xff; /* only use low byte -- see above */
2566 level->fieldx = SP_LEVEL_XSIZE;
2567 level->fieldy = SP_LEVEL_YSIZE;
2569 level->time = 0; /* no time limit */
2570 level->amoeba_speed = 0;
2571 level->time_magic_wall = 0;
2572 level->time_wheel = 0;
2573 level->amoeba_content = EL_EMPTY;
2575 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2576 level->score[i] = 0; /* !!! CORRECT THIS !!! */
2578 /* there are no yamyams in supaplex levels */
2579 for (i = 0; i < level->num_yamyam_contents; i++)
2580 for (y = 0; y < 3; y++)
2581 for (x = 0; x < 3; x++)
2582 level->yamyam_content[i].e[x][y] = EL_EMPTY;
2585 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2586 struct LevelFileInfo *level_file_info)
2588 char *filename = level_file_info->filename;
2590 int nr = level_file_info->nr - leveldir_current->first_level;
2592 char name_first, name_last;
2593 struct LevelInfo multipart_level;
2594 int multipart_xpos, multipart_ypos;
2595 boolean is_multipart_level;
2596 boolean is_first_part;
2597 boolean reading_multipart_level = FALSE;
2598 boolean use_empty_level = FALSE;
2600 if (!(file = fopen(filename, MODE_READ)))
2602 level->no_valid_file = TRUE;
2604 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2609 /* position file stream to the requested level inside the level package */
2610 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2612 level->no_valid_file = TRUE;
2614 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2619 /* there exist Supaplex level package files with multi-part levels which
2620 can be detected as follows: instead of leading and trailing dashes ('-')
2621 to pad the level name, they have leading and trailing numbers which are
2622 the x and y coordinations of the current part of the multi-part level;
2623 if there are '?' characters instead of numbers on the left or right side
2624 of the level name, the multi-part level consists of only horizontal or
2627 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2629 LoadLevelFromFileStream_SP(file, level, l);
2631 /* check if this level is a part of a bigger multi-part level */
2633 name_first = level->name[0];
2634 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
2636 is_multipart_level =
2637 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2638 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
2641 ((name_first == '?' || name_first == '1') &&
2642 (name_last == '?' || name_last == '1'));
2644 /* correct leading multipart level meta information in level name */
2645 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2646 level->name[i] = '-';
2648 /* correct trailing multipart level meta information in level name */
2649 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2650 level->name[i] = '-';
2652 /* ---------- check for normal single level ---------- */
2654 if (!reading_multipart_level && !is_multipart_level)
2656 /* the current level is simply a normal single-part level, and we are
2657 not reading a multi-part level yet, so return the level as it is */
2662 /* ---------- check for empty level (unused multi-part) ---------- */
2664 if (!reading_multipart_level && is_multipart_level && !is_first_part)
2666 /* this is a part of a multi-part level, but not the first part
2667 (and we are not already reading parts of a multi-part level);
2668 in this case, use an empty level instead of the single part */
2670 use_empty_level = TRUE;
2675 /* ---------- check for finished multi-part level ---------- */
2677 if (reading_multipart_level &&
2678 (!is_multipart_level ||
2679 strcmp(level->name, multipart_level.name) != 0))
2681 /* we are already reading parts of a multi-part level, but this level is
2682 either not a multi-part level, or a part of a different multi-part
2683 level; in both cases, the multi-part level seems to be complete */
2688 /* ---------- here we have one part of a multi-part level ---------- */
2690 reading_multipart_level = TRUE;
2692 if (is_first_part) /* start with first part of new multi-part level */
2694 /* copy level info structure from first part */
2695 multipart_level = *level;
2697 /* clear playfield of new multi-part level */
2698 for (y = 0; y < MAX_LEV_FIELDY; y++)
2699 for (x = 0; x < MAX_LEV_FIELDX; x++)
2700 multipart_level.field[x][y] = EL_EMPTY;
2703 if (name_first == '?')
2705 if (name_last == '?')
2708 multipart_xpos = (int)(name_first - '0');
2709 multipart_ypos = (int)(name_last - '0');
2712 printf("----------> part (%d/%d) of multi-part level '%s'\n",
2713 multipart_xpos, multipart_ypos, multipart_level.name);
2716 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2717 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2719 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2724 multipart_level.fieldx = MAX(multipart_level.fieldx,
2725 multipart_xpos * SP_LEVEL_XSIZE);
2726 multipart_level.fieldy = MAX(multipart_level.fieldy,
2727 multipart_ypos * SP_LEVEL_YSIZE);
2729 /* copy level part at the right position of multi-part level */
2730 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2732 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2734 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2735 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2737 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2744 if (use_empty_level)
2746 setLevelInfoToDefaults(level);
2748 level->fieldx = SP_LEVEL_XSIZE;
2749 level->fieldy = SP_LEVEL_YSIZE;
2751 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2752 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2753 level->field[x][y] = EL_EMPTY;
2755 strcpy(level->name, "-------- EMPTY --------");
2757 Error(ERR_WARN, "single part of multi-part level -- using empty level");
2760 if (reading_multipart_level)
2761 *level = multipart_level;
2764 /* ------------------------------------------------------------------------- */
2765 /* functions for loading generic level */
2766 /* ------------------------------------------------------------------------- */
2768 void LoadLevelFromFileInfo(struct LevelInfo *level,
2769 struct LevelFileInfo *level_file_info)
2771 /* always start with reliable default values */
2772 setLevelInfoToDefaults(level);
2774 switch (level_file_info->type)
2776 case LEVEL_FILE_TYPE_RND:
2777 LoadLevelFromFileInfo_RND(level, level_file_info);
2780 case LEVEL_FILE_TYPE_EM:
2781 LoadLevelFromFileInfo_EM(level, level_file_info);
2782 level->game_engine_type = GAME_ENGINE_TYPE_EM;
2785 case LEVEL_FILE_TYPE_SP:
2786 LoadLevelFromFileInfo_SP(level, level_file_info);
2790 LoadLevelFromFileInfo_RND(level, level_file_info);
2794 /* if level file is invalid, restore level structure to default values */
2795 if (level->no_valid_file)
2796 setLevelInfoToDefaults(level);
2798 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2799 level->game_engine_type = GAME_ENGINE_TYPE_RND;
2801 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
2802 CopyNativeLevel_RND_to_Native(level);
2804 CopyNativeLevel_Native_to_RND(level);
2807 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2809 static struct LevelFileInfo level_file_info;
2811 /* always start with reliable default values */
2812 setFileInfoToDefaults(&level_file_info);
2814 level_file_info.nr = 0; /* unknown level number */
2815 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2816 level_file_info.filename = filename;
2818 LoadLevelFromFileInfo(level, &level_file_info);
2821 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2823 if (leveldir_current == NULL) /* only when dumping level */
2826 if (leveldir_current->latest_engine)
2828 /* ---------- use latest game engine ----------------------------------- */
2830 /* For all levels which are forced to use the latest game engine version
2831 (normally all but user contributed, private and undefined levels), set
2832 the game engine version to the actual version; this allows for actual
2833 corrections in the game engine to take effect for existing, converted
2834 levels (from "classic" or other existing games) to make the emulation
2835 of the corresponding game more accurate, while (hopefully) not breaking
2836 existing levels created from other players. */
2838 level->game_version = GAME_VERSION_ACTUAL;
2840 /* Set special EM style gems behaviour: EM style gems slip down from
2841 normal, steel and growing wall. As this is a more fundamental change,
2842 it seems better to set the default behaviour to "off" (as it is more
2843 natural) and make it configurable in the level editor (as a property
2844 of gem style elements). Already existing converted levels (neither
2845 private nor contributed levels) are changed to the new behaviour. */
2847 if (level->file_version < FILE_VERSION_2_0)
2848 level->em_slippery_gems = TRUE;
2853 /* ---------- use game engine the level was created with ----------------- */
2855 /* For all levels which are not forced to use the latest game engine
2856 version (normally user contributed, private and undefined levels),
2857 use the version of the game engine the levels were created for.
2859 Since 2.0.1, the game engine version is now directly stored
2860 in the level file (chunk "VERS"), so there is no need anymore
2861 to set the game version from the file version (except for old,
2862 pre-2.0 levels, where the game version is still taken from the
2863 file format version used to store the level -- see above). */
2865 /* player was faster than enemies in 1.0.0 and before */
2866 if (level->file_version == FILE_VERSION_1_0)
2867 level->double_speed = TRUE;
2869 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2870 if (level->game_version == VERSION_IDENT(2,0,1,0))
2871 level->em_slippery_gems = TRUE;
2873 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2874 if (level->game_version < VERSION_IDENT(2,2,0,0))
2875 level->use_spring_bug = TRUE;
2877 /* time orb caused limited time in endless time levels before 3.2.0-5 */
2878 if (level->game_version < VERSION_IDENT(3,2,0,5))
2879 level->use_time_orb_bug = TRUE;
2881 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
2882 if (level->game_version < VERSION_IDENT(3,2,0,5))
2883 level->block_snap_field = FALSE;
2885 /* only few elements were able to actively move into acid before 3.1.0 */
2886 /* trigger settings did not exist before 3.1.0; set to default "any" */
2887 if (level->game_version < VERSION_IDENT(3,1,0,0))
2891 /* correct "can move into acid" settings (all zero in old levels) */
2893 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2894 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2896 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2897 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2898 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2899 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2901 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2902 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2904 /* correct trigger settings (stored as zero == "none" in old levels) */
2906 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2908 int element = EL_CUSTOM_START + i;
2909 struct ElementInfo *ei = &element_info[element];
2911 for (j = 0; j < ei->num_change_pages; j++)
2913 struct ElementChangeInfo *change = &ei->change_page[j];
2915 change->trigger_player = CH_PLAYER_ANY;
2916 change->trigger_page = CH_PAGE_ANY;
2922 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2926 /* map custom element change events that have changed in newer versions
2927 (these following values were accidentally changed in version 3.0.1)
2928 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
2929 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2931 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2933 int element = EL_CUSTOM_START + i;
2935 /* order of checking and copying events to be mapped is important */
2936 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
2938 if (HAS_CHANGE_EVENT(element, j - 2))
2940 SET_CHANGE_EVENT(element, j - 2, FALSE);
2941 SET_CHANGE_EVENT(element, j, TRUE);
2945 /* order of checking and copying events to be mapped is important */
2946 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
2948 if (HAS_CHANGE_EVENT(element, j - 1))
2950 SET_CHANGE_EVENT(element, j - 1, FALSE);
2951 SET_CHANGE_EVENT(element, j, TRUE);
2957 /* initialize "can_change" field for old levels with only one change page */
2958 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2960 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2962 int element = EL_CUSTOM_START + i;
2964 if (CAN_CHANGE(element))
2965 element_info[element].change->can_change = TRUE;
2969 /* correct custom element values (for old levels without these options) */
2970 if (level->game_version < VERSION_IDENT(3,1,1,0))
2972 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974 int element = EL_CUSTOM_START + i;
2975 struct ElementInfo *ei = &element_info[element];
2977 if (ei->access_direction == MV_NO_DIRECTION)
2978 ei->access_direction = MV_ALL_DIRECTIONS;
2981 for (j = 0; j < ei->num_change_pages; j++)
2983 struct ElementChangeInfo *change = &ei->change_page[j];
2985 if (change->trigger_side == CH_SIDE_NONE)
2986 change->trigger_side = CH_SIDE_ANY;
2993 /* correct custom element values (fix invalid values for all versions) */
2996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2998 int element = EL_CUSTOM_START + i;
2999 struct ElementInfo *ei = &element_info[element];
3001 for (j = 0; j < ei->num_change_pages; j++)
3003 struct ElementChangeInfo *change = &ei->change_page[j];
3005 if (change->trigger_player == CH_PLAYER_NONE)
3006 change->trigger_player = CH_PLAYER_ANY;
3008 if (change->trigger_side == CH_SIDE_NONE)
3009 change->trigger_side = CH_SIDE_ANY;
3015 /* initialize "can_explode" field for old levels which did not store this */
3016 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
3017 if (level->game_version <= VERSION_IDENT(3,1,0,0))
3019 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3021 int element = EL_CUSTOM_START + i;
3023 if (EXPLODES_1X1_OLD(element))
3024 element_info[element].explosion_type = EXPLODES_1X1;
3026 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
3027 EXPLODES_SMASHED(element) ||
3028 EXPLODES_IMPACT(element)));
3032 /* correct previously hard-coded move delay values for maze runner style */
3033 if (level->game_version < VERSION_IDENT(3,1,1,0))
3035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3037 int element = EL_CUSTOM_START + i;
3039 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3041 /* previously hard-coded and therefore ignored */
3042 element_info[element].move_delay_fixed = 9;
3043 element_info[element].move_delay_random = 0;
3048 /* map elements that have changed in newer versions */
3049 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3050 level->game_version);
3051 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3052 for (x = 0; x < 3; x++)
3053 for (y = 0; y < 3; y++)
3054 level->yamyam_content[i].e[x][y] =
3055 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
3056 level->game_version);
3058 /* initialize element properties for level editor etc. */
3059 InitElementPropertiesEngine(level->game_version);
3062 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3066 /* map elements that have changed in newer versions */
3067 for (y = 0; y < level->fieldy; y++)
3068 for (x = 0; x < level->fieldx; x++)
3069 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
3070 level->game_version);
3072 /* copy elements to runtime playfield array */
3073 for (x = 0; x < MAX_LEV_FIELDX; x++)
3074 for (y = 0; y < MAX_LEV_FIELDY; y++)
3075 Feld[x][y] = level->field[x][y];
3077 /* initialize level size variables for faster access */
3078 lev_fieldx = level->fieldx;
3079 lev_fieldy = level->fieldy;
3081 /* determine border element for this level */
3085 void LoadLevelTemplate(int nr)
3089 setLevelFileInfo(&level_template.file_info, nr);
3090 filename = level_template.file_info.filename;
3092 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3094 LoadLevel_InitVersion(&level_template, filename);
3095 LoadLevel_InitElements(&level_template, filename);
3097 ActivateLevelTemplate();
3100 void LoadLevel(int nr)
3104 setLevelFileInfo(&level.file_info, nr);
3105 filename = level.file_info.filename;
3107 LoadLevelFromFileInfo(&level, &level.file_info);
3109 if (level.use_custom_template)
3110 LoadLevelTemplate(-1);
3112 LoadLevel_InitVersion(&level, filename);
3113 LoadLevel_InitElements(&level, filename);
3114 LoadLevel_InitPlayfield(&level, filename);
3117 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3119 putFileVersion(file, level->file_version);
3120 putFileVersion(file, level->game_version);
3123 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3127 putFile8Bit(file, level->fieldx);
3128 putFile8Bit(file, level->fieldy);
3130 putFile16BitBE(file, level->time);
3131 putFile16BitBE(file, level->gems_needed);
3133 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3134 putFile8Bit(file, level->name[i]);
3136 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3137 putFile8Bit(file, level->score[i]);
3139 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3140 for (y = 0; y < 3; y++)
3141 for (x = 0; x < 3; x++)
3142 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3143 level->yamyam_content[i].e[x][y]));
3144 putFile8Bit(file, level->amoeba_speed);
3145 putFile8Bit(file, level->time_magic_wall);
3146 putFile8Bit(file, level->time_wheel);
3147 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3148 level->amoeba_content));
3149 putFile8Bit(file, (level->double_speed ? 1 : 0));
3150 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3151 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3152 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3154 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3156 putFile8Bit(file, (level->block_last_field ? 1 : 0));
3157 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3158 putFile32BitBE(file, level->can_move_into_acid_bits);
3159 putFile8Bit(file, level->dont_collide_with_bits);
3161 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3162 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3164 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3165 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3166 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3168 putFile8Bit(file, level->game_engine_type);
3170 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3173 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3177 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3178 putFile8Bit(file, level->author[i]);
3181 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3185 for (y = 0; y < level->fieldy; y++)
3186 for (x = 0; x < level->fieldx; x++)
3187 if (level->encoding_16bit_field)
3188 putFile16BitBE(file, level->field[x][y]);
3190 putFile8Bit(file, level->field[x][y]);
3194 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3198 putFile8Bit(file, EL_YAMYAM);
3199 putFile8Bit(file, level->num_yamyam_contents);
3200 putFile8Bit(file, 0);
3201 putFile8Bit(file, 0);
3203 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3204 for (y = 0; y < 3; y++)
3205 for (x = 0; x < 3; x++)
3206 if (level->encoding_16bit_field)
3207 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
3209 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
3213 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3216 int num_contents, content_xsize, content_ysize;
3217 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3219 if (element == EL_YAMYAM)
3221 num_contents = level->num_yamyam_contents;
3225 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3226 for (y = 0; y < 3; y++)
3227 for (x = 0; x < 3; x++)
3228 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
3230 else if (element == EL_BD_AMOEBA)
3236 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3237 for (y = 0; y < 3; y++)
3238 for (x = 0; x < 3; x++)
3239 content_array[i][x][y] = EL_EMPTY;
3240 content_array[0][0][0] = level->amoeba_content;
3244 /* chunk header already written -- write empty chunk data */
3245 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3247 Error(ERR_WARN, "cannot save content for element '%d'", element);
3251 putFile16BitBE(file, element);
3252 putFile8Bit(file, num_contents);
3253 putFile8Bit(file, content_xsize);
3254 putFile8Bit(file, content_ysize);
3256 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3258 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3259 for (y = 0; y < 3; y++)
3260 for (x = 0; x < 3; x++)
3261 putFile16BitBE(file, content_array[i][x][y]);
3264 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3267 int envelope_nr = element - EL_ENVELOPE_1;
3268 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3270 putFile16BitBE(file, element);
3271 putFile16BitBE(file, envelope_len);
3272 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3273 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3275 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3277 for (i = 0; i < envelope_len; i++)
3278 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3282 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3283 int num_changed_custom_elements)
3287 putFile16BitBE(file, num_changed_custom_elements);
3289 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3291 int element = EL_CUSTOM_START + i;
3293 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3295 if (check < num_changed_custom_elements)
3297 putFile16BitBE(file, element);
3298 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3305 if (check != num_changed_custom_elements) /* should not happen */
3306 Error(ERR_WARN, "inconsistent number of custom element properties");
3311 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3312 int num_changed_custom_elements)
3316 putFile16BitBE(file, num_changed_custom_elements);
3318 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3320 int element = EL_CUSTOM_START + i;
3322 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3324 if (check < num_changed_custom_elements)
3326 putFile16BitBE(file, element);
3327 putFile16BitBE(file, element_info[element].change->target_element);
3334 if (check != num_changed_custom_elements) /* should not happen */
3335 Error(ERR_WARN, "inconsistent number of custom target elements");
3340 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3341 int num_changed_custom_elements)
3343 int i, j, x, y, check = 0;
3345 putFile16BitBE(file, num_changed_custom_elements);
3347 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3349 int element = EL_CUSTOM_START + i;
3350 struct ElementInfo *ei = &element_info[element];
3352 if (ei->modified_settings)
3354 if (check < num_changed_custom_elements)
3356 putFile16BitBE(file, element);
3358 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3359 putFile8Bit(file, ei->description[j]);
3361 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3363 /* some free bytes for future properties and padding */
3364 WriteUnusedBytesToFile(file, 7);
3366 putFile8Bit(file, ei->use_gfx_element);
3367 putFile16BitBE(file, ei->gfx_element);
3369 putFile8Bit(file, ei->collect_score_initial);
3370 putFile8Bit(file, ei->collect_count_initial);
3372 putFile16BitBE(file, ei->push_delay_fixed);
3373 putFile16BitBE(file, ei->push_delay_random);
3374 putFile16BitBE(file, ei->move_delay_fixed);
3375 putFile16BitBE(file, ei->move_delay_random);
3377 putFile16BitBE(file, ei->move_pattern);
3378 putFile8Bit(file, ei->move_direction_initial);
3379 putFile8Bit(file, ei->move_stepsize);
3381 for (y = 0; y < 3; y++)
3382 for (x = 0; x < 3; x++)
3383 putFile16BitBE(file, ei->content.e[x][y]);
3385 putFile32BitBE(file, ei->change->events);
3387 putFile16BitBE(file, ei->change->target_element);
3389 putFile16BitBE(file, ei->change->delay_fixed);
3390 putFile16BitBE(file, ei->change->delay_random);
3391 putFile16BitBE(file, ei->change->delay_frames);
3393 putFile16BitBE(file, ei->change->trigger_element);
3395 putFile8Bit(file, ei->change->explode);
3396 putFile8Bit(file, ei->change->use_target_content);
3397 putFile8Bit(file, ei->change->only_if_complete);
3398 putFile8Bit(file, ei->change->use_random_replace);
3400 putFile8Bit(file, ei->change->random_percentage);
3401 putFile8Bit(file, ei->change->replace_when);
3403 for (y = 0; y < 3; y++)
3404 for (x = 0; x < 3; x++)
3405 putFile16BitBE(file, ei->change->content.e[x][y]);
3407 putFile8Bit(file, ei->slippery_type);
3409 /* some free bytes for future properties and padding */
3410 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3417 if (check != num_changed_custom_elements) /* should not happen */
3418 Error(ERR_WARN, "inconsistent number of custom element properties");
3422 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3424 struct ElementInfo *ei = &element_info[element];
3427 /* ---------- custom element base property values (96 bytes) ------------- */
3429 putFile16BitBE(file, element);
3431 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3432 putFile8Bit(file, ei->description[i]);
3434 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3435 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
3437 putFile8Bit(file, ei->num_change_pages);
3439 putFile16BitBE(file, ei->ce_value_fixed_initial);
3440 putFile16BitBE(file, ei->ce_value_random_initial);
3441 putFile8Bit(file, ei->use_last_ce_value);
3443 putFile8Bit(file, ei->use_gfx_element);
3444 putFile16BitBE(file, ei->gfx_element);
3446 putFile8Bit(file, ei->collect_score_initial);
3447 putFile8Bit(file, ei->collect_count_initial);
3449 putFile8Bit(file, ei->drop_delay_fixed);
3450 putFile8Bit(file, ei->push_delay_fixed);
3451 putFile8Bit(file, ei->drop_delay_random);
3452 putFile8Bit(file, ei->push_delay_random);
3453 putFile16BitBE(file, ei->move_delay_fixed);
3454 putFile16BitBE(file, ei->move_delay_random);
3456 /* bits 0 - 15 of "move_pattern" ... */
3457 putFile16BitBE(file, ei->move_pattern & 0xffff);
3458 putFile8Bit(file, ei->move_direction_initial);
3459 putFile8Bit(file, ei->move_stepsize);
3461 putFile8Bit(file, ei->slippery_type);
3463 for (y = 0; y < 3; y++)
3464 for (x = 0; x < 3; x++)
3465 putFile16BitBE(file, ei->content.e[x][y]);
3467 putFile16BitBE(file, ei->move_enter_element);
3468 putFile16BitBE(file, ei->move_leave_element);
3469 putFile8Bit(file, ei->move_leave_type);
3471 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3472 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3474 putFile8Bit(file, ei->access_direction);
3476 putFile8Bit(file, ei->explosion_delay);
3477 putFile8Bit(file, ei->ignition_delay);
3478 putFile8Bit(file, ei->explosion_type);
3480 /* some free bytes for future custom property values and padding */
3481 WriteUnusedBytesToFile(file, 1);
3483 /* ---------- change page property values (48 bytes) --------------------- */
3485 for (i = 0; i < ei->num_change_pages; i++)
3487 struct ElementChangeInfo *change = &ei->change_page[i];
3488 unsigned long event_bits = 0;
3490 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3491 if (change->has_event[j])
3492 event_bits |= (1 << j);
3494 putFile32BitBE(file, event_bits);
3496 putFile16BitBE(file, change->target_element);
3498 putFile16BitBE(file, change->delay_fixed);
3499 putFile16BitBE(file, change->delay_random);
3500 putFile16BitBE(file, change->delay_frames);
3502 putFile16BitBE(file, change->trigger_element);
3504 putFile8Bit(file, change->explode);
3505 putFile8Bit(file, change->use_target_content);
3506 putFile8Bit(file, change->only_if_complete);
3507 putFile8Bit(file, change->use_random_replace);
3509 putFile8Bit(file, change->random_percentage);
3510 putFile8Bit(file, change->replace_when);
3512 for (y = 0; y < 3; y++)
3513 for (x = 0; x < 3; x++)
3514 putFile16BitBE(file, change->target_content.e[x][y]);
3516 putFile8Bit(file, change->can_change);
3518 putFile8Bit(file, change->trigger_side);
3520 putFile8Bit(file, change->trigger_player);
3521 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3522 log_2(change->trigger_page)));
3524 putFile8Bit(file, change->has_action);
3525 putFile8Bit(file, change->action_type);
3526 putFile8Bit(file, change->action_mode);
3527 putFile16BitBE(file, change->action_arg);
3529 /* some free bytes for future change property values and padding */
3530 WriteUnusedBytesToFile(file, 1);
3534 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3536 struct ElementInfo *ei = &element_info[element];
3537 struct ElementGroupInfo *group = ei->group;
3540 putFile16BitBE(file, element);
3542 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3543 putFile8Bit(file, ei->description[i]);
3545 putFile8Bit(file, group->num_elements);
3547 putFile8Bit(file, ei->use_gfx_element);
3548 putFile16BitBE(file, ei->gfx_element);
3550 putFile8Bit(file, group->choice_mode);
3552 /* some free bytes for future values and padding */
3553 WriteUnusedBytesToFile(file, 3);
3555 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3556 putFile16BitBE(file, group->element[i]);
3559 static int SaveLevel_CONF_Value(FILE *file, int pos)
3561 int default_value = element_conf[pos].default_value;
3562 int element = element_conf[pos].element;
3563 int type = element_conf[pos].type;
3564 int bytes = type & CONF_MASK_BYTES;
3565 void *value_ptr = element_conf[pos].value;
3566 int value = (CONF_VALUE_BOOLEAN(type) ? *(boolean *)value_ptr :
3569 boolean modified = FALSE;
3571 /* check if any settings have been modified before saving them */
3572 if (value != default_value)
3575 if (!modified) /* do not save unmodified default settings */
3578 if (bytes == CONF_MASK_MULTI_BYTES)
3579 Error(ERR_EXIT, "SaveLevel_CONF_Value: cannot save multi-byte values");
3581 num_bytes += putFile16BitBE(file, element);
3582 num_bytes += putFile8Bit(file, type);
3583 num_bytes += (bytes == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
3584 bytes == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
3585 bytes == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) : 0);
3590 static int SaveLevel_CONF_Content(FILE *file, int pos, int num_contents)
3592 struct Content *content = (struct Content *)(element_conf[pos].value);
3593 int default_value = element_conf[pos].default_value;
3594 int element = element_conf[pos].element;
3595 int type = element_conf[pos].type;
3597 boolean modified = FALSE;
3600 /* check if any settings have been modified before saving them */
3601 for (i = 0; i < num_contents; i++)
3602 for (y = 0; y < 3; y++)
3603 for (x = 0; x < 3; x++)
3604 if (content[i].e[x][y] != default_value)
3607 if (!modified) /* do not save unmodified default settings */
3610 num_bytes += putFile16BitBE(file, element);
3611 num_bytes += putFile8Bit(file, type);
3612 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
3614 for (i = 0; i < num_contents; i++)
3615 for (y = 0; y < 3; y++)
3616 for (x = 0; x < 3; x++)
3617 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
3622 static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
3627 li = *level; /* copy level information into temporary buffer */
3629 for (i = 0; element_conf[i].element != -1; i++)
3631 int type = element_conf[i].type;
3632 int bytes = type & CONF_MASK_BYTES;
3634 if (bytes != CONF_MASK_MULTI_BYTES)
3635 chunk_size += SaveLevel_CONF_Value(file, i);
3636 else if (type == CONF_VALUE_CONTENT_8)
3637 chunk_size += SaveLevel_CONF_Content(file, i, MAX_ELEMENT_CONTENTS);
3643 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3645 int body_chunk_size, conf_chunk_size;
3649 if (!(file = fopen(filename, MODE_WRITE)))
3651 Error(ERR_WARN, "cannot save level file '%s'", filename);
3655 level->file_version = FILE_VERSION_ACTUAL;
3656 level->game_version = GAME_VERSION_ACTUAL;
3658 /* check level field for 16-bit elements */
3659 level->encoding_16bit_field = FALSE;
3660 for (y = 0; y < level->fieldy; y++)
3661 for (x = 0; x < level->fieldx; x++)
3662 if (level->field[x][y] > 255)
3663 level->encoding_16bit_field = TRUE;
3665 /* check yamyam content for 16-bit elements */
3666 level->encoding_16bit_yamyam = FALSE;
3667 for (i = 0; i < level->num_yamyam_contents; i++)
3668 for (y = 0; y < 3; y++)
3669 for (x = 0; x < 3; x++)
3670 if (level->yamyam_content[i].e[x][y] > 255)
3671 level->encoding_16bit_yamyam = TRUE;
3673 /* check amoeba content for 16-bit elements */
3674 level->encoding_16bit_amoeba = FALSE;
3675 if (level->amoeba_content > 255)
3676 level->encoding_16bit_amoeba = TRUE;
3678 /* calculate size of "BODY" chunk */
3680 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3682 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3683 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3685 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3686 SaveLevel_VERS(file, level);
3688 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3689 SaveLevel_HEAD(file, level);
3691 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3692 SaveLevel_AUTH(file, level);
3694 putFileChunkBE(file, "BODY", body_chunk_size);
3695 SaveLevel_BODY(file, level);
3697 if (level->encoding_16bit_yamyam ||
3698 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3700 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3701 SaveLevel_CNT2(file, level, EL_YAMYAM);
3704 if (level->encoding_16bit_amoeba)
3706 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3707 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3710 /* check for envelope content */
3711 for (i = 0; i < 4; i++)
3713 if (strlen(level->envelope_text[i]) > 0)
3715 int envelope_len = strlen(level->envelope_text[i]) + 1;
3717 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3718 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3722 /* check for non-default custom elements (unless using template level) */
3723 if (!level->use_custom_template)
3725 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3727 int element = EL_CUSTOM_START + i;
3729 if (element_info[element].modified_settings)
3731 int num_change_pages = element_info[element].num_change_pages;
3733 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3734 SaveLevel_CUS4(file, level, element);
3739 /* check for non-default group elements (unless using template level) */
3740 if (!level->use_custom_template)
3742 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3744 int element = EL_GROUP_START + i;
3746 if (element_info[element].modified_settings)
3748 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3749 SaveLevel_GRP1(file, level, element);
3754 conf_chunk_size = SaveLevel_CONF(NULL, level); /* get chunk size */
3756 /* check for non-default configuration settings to be saved in CONF chunk */
3757 if (conf_chunk_size > 0)
3759 putFileChunkBE(file, "CONF", conf_chunk_size);
3760 SaveLevel_CONF(file, level);
3765 SetFilePermissions(filename, PERMS_PRIVATE);
3768 void SaveLevel(int nr)
3770 char *filename = getDefaultLevelFilename(nr);
3772 SaveLevelFromFilename(&level, filename);
3775 void SaveLevelTemplate()
3777 char *filename = getDefaultLevelFilename(-1);
3779 SaveLevelFromFilename(&level, filename);
3782 void DumpLevel(struct LevelInfo *level)
3784 if (level->no_valid_file)
3786 Error(ERR_WARN, "cannot dump -- no valid level file found");
3791 printf_line("-", 79);
3792 printf("Level xxx (file version %08d, game version %08d)\n",
3793 level->file_version, level->game_version);
3794 printf_line("-", 79);
3796 printf("Level author: '%s'\n", level->author);
3797 printf("Level title: '%s'\n", level->name);
3799 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3801 printf("Level time: %d seconds\n", level->time);
3802 printf("Gems needed: %d\n", level->gems_needed);
3804 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3805 printf("Time for wheel: %d seconds\n", level->time_wheel);
3806 printf("Time for light: %d seconds\n", level->time_light);
3807 printf("Time for timegate: %d seconds\n", level->time_timegate);
3809 printf("Amoeba speed: %d\n", level->amoeba_speed);
3811 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3812 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3813 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3814 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3815 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3816 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3817 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3819 printf_line("-", 79);
3823 /* ========================================================================= */
3824 /* tape file functions */
3825 /* ========================================================================= */
3827 static void setTapeInfoToDefaults()
3831 /* always start with reliable default values (empty tape) */
3834 /* default values (also for pre-1.2 tapes) with only the first player */
3835 tape.player_participates[0] = TRUE;
3836 for (i = 1; i < MAX_PLAYERS; i++)
3837 tape.player_participates[i] = FALSE;
3839 /* at least one (default: the first) player participates in every tape */
3840 tape.num_participating_players = 1;
3842 tape.level_nr = level_nr;
3844 tape.changed = FALSE;
3846 tape.recording = FALSE;
3847 tape.playing = FALSE;
3848 tape.pausing = FALSE;
3850 tape.no_valid_file = FALSE;
3853 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3855 tape->file_version = getFileVersion(file);
3856 tape->game_version = getFileVersion(file);
3861 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3865 tape->random_seed = getFile32BitBE(file);
3866 tape->date = getFile32BitBE(file);
3867 tape->length = getFile32BitBE(file);
3869 /* read header fields that are new since version 1.2 */
3870 if (tape->file_version >= FILE_VERSION_1_2)
3872 byte store_participating_players = getFile8Bit(file);
3875 /* since version 1.2, tapes store which players participate in the tape */
3876 tape->num_participating_players = 0;
3877 for (i = 0; i < MAX_PLAYERS; i++)
3879 tape->player_participates[i] = FALSE;
3881 if (store_participating_players & (1 << i))
3883 tape->player_participates[i] = TRUE;
3884 tape->num_participating_players++;
3888 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3890 engine_version = getFileVersion(file);
3891 if (engine_version > 0)
3892 tape->engine_version = engine_version;
3894 tape->engine_version = tape->game_version;
3900 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3902 int level_identifier_size;
3905 level_identifier_size = getFile16BitBE(file);
3907 tape->level_identifier =
3908 checked_realloc(tape->level_identifier, level_identifier_size);
3910 for (i = 0; i < level_identifier_size; i++)
3911 tape->level_identifier[i] = getFile8Bit(file);
3913 tape->level_nr = getFile16BitBE(file);
3915 chunk_size = 2 + level_identifier_size + 2;
3920 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3923 int chunk_size_expected =
3924 (tape->num_participating_players + 1) * tape->length;
3926 if (chunk_size_expected != chunk_size)
3928 ReadUnusedBytesFromFile(file, chunk_size);
3929 return chunk_size_expected;
3932 for (i = 0; i < tape->length; i++)
3934 if (i >= MAX_TAPE_LEN)
3937 for (j = 0; j < MAX_PLAYERS; j++)
3939 tape->pos[i].action[j] = MV_NONE;
3941 if (tape->player_participates[j])
3942 tape->pos[i].action[j] = getFile8Bit(file);
3945 tape->pos[i].delay = getFile8Bit(file);
3947 if (tape->file_version == FILE_VERSION_1_0)
3949 /* eliminate possible diagonal moves in old tapes */
3950 /* this is only for backward compatibility */
3952 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3953 byte action = tape->pos[i].action[0];
3954 int k, num_moves = 0;
3956 for (k = 0; k<4; k++)
3958 if (action & joy_dir[k])
3960 tape->pos[i + num_moves].action[0] = joy_dir[k];
3962 tape->pos[i + num_moves].delay = 0;
3971 tape->length += num_moves;
3974 else if (tape->file_version < FILE_VERSION_2_0)
3976 /* convert pre-2.0 tapes to new tape format */
3978 if (tape->pos[i].delay > 1)
3981 tape->pos[i + 1] = tape->pos[i];
3982 tape->pos[i + 1].delay = 1;
3985 for (j = 0; j < MAX_PLAYERS; j++)
3986 tape->pos[i].action[j] = MV_NONE;
3987 tape->pos[i].delay--;
3998 if (i != tape->length)
3999 chunk_size = (tape->num_participating_players + 1) * i;
4004 void LoadTapeFromFilename(char *filename)
4006 char cookie[MAX_LINE_LEN];
4007 char chunk_name[CHUNK_ID_LEN + 1];
4011 /* always start with reliable default values */
4012 setTapeInfoToDefaults();
4014 if (!(file = fopen(filename, MODE_READ)))
4016 tape.no_valid_file = TRUE;
4021 getFileChunkBE(file, chunk_name, NULL);
4022 if (strcmp(chunk_name, "RND1") == 0)
4024 getFile32BitBE(file); /* not used */
4026 getFileChunkBE(file, chunk_name, NULL);
4027 if (strcmp(chunk_name, "TAPE") != 0)
4029 tape.no_valid_file = TRUE;
4031 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4036 else /* check for pre-2.0 file format with cookie string */
4038 strcpy(cookie, chunk_name);
4039 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4040 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4041 cookie[strlen(cookie) - 1] = '\0';
4043 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4045 tape.no_valid_file = TRUE;
4047 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4052 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4054 tape.no_valid_file = TRUE;
4056 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4061 /* pre-2.0 tape files have no game version, so use file version here */
4062 tape.game_version = tape.file_version;
4065 if (tape.file_version < FILE_VERSION_1_2)
4067 /* tape files from versions before 1.2.0 without chunk structure */
4068 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4069 LoadTape_BODY(file, 2 * tape.length, &tape);
4077 int (*loader)(FILE *, int, struct TapeInfo *);
4081 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
4082 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
4083 { "INFO", -1, LoadTape_INFO },
4084 { "BODY", -1, LoadTape_BODY },
4088 while (getFileChunkBE(file, chunk_name, &chunk_size))
4092 while (chunk_info[i].name != NULL &&
4093 strcmp(chunk_name, chunk_info[i].name) != 0)
4096 if (chunk_info[i].name == NULL)
4098 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4099 chunk_name, filename);
4100 ReadUnusedBytesFromFile(file, chunk_size);
4102 else if (chunk_info[i].size != -1 &&
4103 chunk_info[i].size != chunk_size)
4105 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4106 chunk_size, chunk_name, filename);
4107 ReadUnusedBytesFromFile(file, chunk_size);
4111 /* call function to load this tape chunk */
4112 int chunk_size_expected =
4113 (chunk_info[i].loader)(file, chunk_size, &tape);
4115 /* the size of some chunks cannot be checked before reading other
4116 chunks first (like "HEAD" and "BODY") that contain some header
4117 information, so check them here */
4118 if (chunk_size_expected != chunk_size)
4120 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4121 chunk_size, chunk_name, filename);
4129 tape.length_seconds = GetTapeLength();
4132 printf("::: tape game version: %d\n", tape.game_version);
4133 printf("::: tape engine version: %d\n", tape.engine_version);
4137 void LoadTape(int nr)
4139 char *filename = getTapeFilename(nr);
4141 LoadTapeFromFilename(filename);
4144 void LoadSolutionTape(int nr)
4146 char *filename = getSolutionTapeFilename(nr);
4148 LoadTapeFromFilename(filename);
4151 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4153 putFileVersion(file, tape->file_version);
4154 putFileVersion(file, tape->game_version);
4157 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4160 byte store_participating_players = 0;
4162 /* set bits for participating players for compact storage */
4163 for (i = 0; i < MAX_PLAYERS; i++)
4164 if (tape->player_participates[i])
4165 store_participating_players |= (1 << i);
4167 putFile32BitBE(file, tape->random_seed);
4168 putFile32BitBE(file, tape->date);
4169 putFile32BitBE(file, tape->length);
4171 putFile8Bit(file, store_participating_players);
4173 /* unused bytes not at the end here for 4-byte alignment of engine_version */
4174 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4176 putFileVersion(file, tape->engine_version);
4179 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4181 int level_identifier_size = strlen(tape->level_identifier) + 1;
4184 putFile16BitBE(file, level_identifier_size);
4186 for (i = 0; i < level_identifier_size; i++)
4187 putFile8Bit(file, tape->level_identifier[i]);
4189 putFile16BitBE(file, tape->level_nr);
4192 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4196 for (i = 0; i < tape->length; i++)
4198 for (j = 0; j < MAX_PLAYERS; j++)
4199 if (tape->player_participates[j])
4200 putFile8Bit(file, tape->pos[i].action[j]);
4202 putFile8Bit(file, tape->pos[i].delay);
4206 void SaveTape(int nr)
4208 char *filename = getTapeFilename(nr);
4210 boolean new_tape = TRUE;
4211 int num_participating_players = 0;
4212 int info_chunk_size;
4213 int body_chunk_size;
4216 InitTapeDirectory(leveldir_current->subdir);
4218 /* if a tape still exists, ask to overwrite it */
4219 if (fileExists(filename))
4222 if (!Request("Replace old tape ?", REQ_ASK))
4226 if (!(file = fopen(filename, MODE_WRITE)))
4228 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4232 tape.file_version = FILE_VERSION_ACTUAL;
4233 tape.game_version = GAME_VERSION_ACTUAL;
4235 /* count number of participating players */
4236 for (i = 0; i < MAX_PLAYERS; i++)
4237 if (tape.player_participates[i])
4238 num_participating_players++;
4240 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4241 body_chunk_size = (num_participating_players + 1) * tape.length;
4243 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4244 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4246 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4247 SaveTape_VERS(file, &tape);
4249 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4250 SaveTape_HEAD(file, &tape);
4252 putFileChunkBE(file, "INFO", info_chunk_size);
4253 SaveTape_INFO(file, &tape);
4255 putFileChunkBE(file, "BODY", body_chunk_size);
4256 SaveTape_BODY(file, &tape);
4260 SetFilePermissions(filename, PERMS_PRIVATE);
4262 tape.changed = FALSE;
4265 Request("Tape saved !", REQ_CONFIRM);
4268 void DumpTape(struct TapeInfo *tape)
4272 if (tape->no_valid_file)
4274 Error(ERR_WARN, "cannot dump -- no valid tape file found");
4279 printf_line("-", 79);
4280 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4281 tape->level_nr, tape->file_version, tape->game_version);
4282 printf(" (effective engine version %08d)\n",
4283 tape->engine_version);
4284 printf("Level series identifier: '%s'\n", tape->level_identifier);
4285 printf_line("-", 79);
4287 for (i = 0; i < tape->length; i++)
4289 if (i >= MAX_TAPE_LEN)
4292 printf("%03d: ", i);
4294 for (j = 0; j < MAX_PLAYERS; j++)
4296 if (tape->player_participates[j])
4298 int action = tape->pos[i].action[j];
4300 printf("%d:%02x ", j, action);
4301 printf("[%c%c%c%c|%c%c] - ",
4302 (action & JOY_LEFT ? '<' : ' '),
4303 (action & JOY_RIGHT ? '>' : ' '),
4304 (action & JOY_UP ? '^' : ' '),
4305 (action & JOY_DOWN ? 'v' : ' '),
4306 (action & JOY_BUTTON_1 ? '1' : ' '),
4307 (action & JOY_BUTTON_2 ? '2' : ' '));
4311 printf("(%03d)\n", tape->pos[i].delay);
4314 printf_line("-", 79);
4318 /* ========================================================================= */
4319 /* score file functions */
4320 /* ========================================================================= */
4322 void LoadScore(int nr)
4325 char *filename = getScoreFilename(nr);
4326 char cookie[MAX_LINE_LEN];
4327 char line[MAX_LINE_LEN];
4331 /* always start with reliable default values */
4332 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4334 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4335 highscore[i].Score = 0;
4338 if (!(file = fopen(filename, MODE_READ)))
4341 /* check file identifier */
4342 fgets(cookie, MAX_LINE_LEN, file);
4343 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4344 cookie[strlen(cookie) - 1] = '\0';
4346 if (!checkCookieString(cookie, SCORE_COOKIE))
4348 Error(ERR_WARN, "unknown format of score file '%s'", filename);
4353 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4355 fscanf(file, "%d", &highscore[i].Score);
4356 fgets(line, MAX_LINE_LEN, file);
4358 if (line[strlen(line) - 1] == '\n')
4359 line[strlen(line) - 1] = '\0';
4361 for (line_ptr = line; *line_ptr; line_ptr++)
4363 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4365 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4366 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4375 void SaveScore(int nr)
4378 char *filename = getScoreFilename(nr);
4381 InitScoreDirectory(leveldir_current->subdir);
4383 if (!(file = fopen(filename, MODE_WRITE)))
4385 Error(ERR_WARN, "cannot save score for level %d", nr);
4389 fprintf(file, "%s\n\n", SCORE_COOKIE);
4391 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4392 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4396 SetFilePermissions(filename, PERMS_PUBLIC);
4400 /* ========================================================================= */
4401 /* setup file functions */
4402 /* ========================================================================= */
4404 #define TOKEN_STR_PLAYER_PREFIX "player_"
4407 #define SETUP_TOKEN_PLAYER_NAME 0
4408 #define SETUP_TOKEN_SOUND 1
4409 #define SETUP_TOKEN_SOUND_LOOPS 2
4410 #define SETUP_TOKEN_SOUND_MUSIC 3
4411 #define SETUP_TOKEN_SOUND_SIMPLE 4
4412 #define SETUP_TOKEN_TOONS 5
4413 #define SETUP_TOKEN_SCROLL_DELAY 6
4414 #define SETUP_TOKEN_SOFT_SCROLLING 7
4415 #define SETUP_TOKEN_FADING 8
4416 #define SETUP_TOKEN_AUTORECORD 9
4417 #define SETUP_TOKEN_QUICK_DOORS 10
4418 #define SETUP_TOKEN_TEAM_MODE 11
4419 #define SETUP_TOKEN_HANDICAP 12
4420 #define SETUP_TOKEN_SKIP_LEVELS 13
4421 #define SETUP_TOKEN_TIME_LIMIT 14
4422 #define SETUP_TOKEN_FULLSCREEN 15
4423 #define SETUP_TOKEN_ASK_ON_ESCAPE 16
4424 #define SETUP_TOKEN_GRAPHICS_SET 17
4425 #define SETUP_TOKEN_SOUNDS_SET 18
4426 #define SETUP_TOKEN_MUSIC_SET 19
4427 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 20
4428 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 21
4429 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 22
4431 #define NUM_GLOBAL_SETUP_TOKENS 23
4434 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
4435 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
4436 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
4437 #define SETUP_TOKEN_EDITOR_EL_MORE 3
4438 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
4439 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
4440 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
4441 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
4442 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
4443 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 9
4444 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 10
4445 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
4446 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
4448 #define NUM_EDITOR_SETUP_TOKENS 13
4450 /* shortcut setup */
4451 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
4452 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
4453 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
4455 #define NUM_SHORTCUT_SETUP_TOKENS 3
4458 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
4459 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
4460 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
4461 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
4462 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
4463 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
4464 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
4465 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
4466 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
4467 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
4468 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
4469 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
4470 #define SETUP_TOKEN_PLAYER_KEY_UP 12
4471 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
4472 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
4473 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
4475 #define NUM_PLAYER_SETUP_TOKENS 16
4478 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
4479 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
4481 #define NUM_SYSTEM_SETUP_TOKENS 2
4484 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
4486 #define NUM_OPTIONS_SETUP_TOKENS 1
4489 static struct SetupInfo si;
4490 static struct SetupEditorInfo sei;
4491 static struct SetupShortcutInfo ssi;
4492 static struct SetupInputInfo sii;
4493 static struct SetupSystemInfo syi;
4494 static struct OptionInfo soi;
4496 static struct TokenInfo global_setup_tokens[] =
4498 { TYPE_STRING, &si.player_name, "player_name" },
4499 { TYPE_SWITCH, &si.sound, "sound" },
4500 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
4501 { TYPE_SWITCH, &si.sound_music, "background_music" },
4502 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
4503 { TYPE_SWITCH, &si.toons, "toons" },
4504 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
4505 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
4506 { TYPE_SWITCH, &si.fading, "screen_fading" },
4507 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
4508 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
4509 { TYPE_SWITCH, &si.team_mode, "team_mode" },
4510 { TYPE_SWITCH, &si.handicap, "handicap" },
4511 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
4512 { TYPE_SWITCH, &si.time_limit, "time_limit" },
4513 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
4514 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
4515 { TYPE_STRING, &si.graphics_set, "graphics_set" },
4516 { TYPE_STRING, &si.sounds_set, "sounds_set" },
4517 { TYPE_STRING, &si.music_set, "music_set" },
4518 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4519 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
4520 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
4523 static struct TokenInfo editor_setup_tokens[] =
4525 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
4526 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
4527 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
4528 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
4529 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
4530 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
4531 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
4532 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
4533 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
4534 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
4535 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
4536 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
4537 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
4540 static struct TokenInfo shortcut_setup_tokens[] =
4542 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
4543 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
4544 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
4547 static struct TokenInfo player_setup_tokens[] =
4549 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
4550 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
4551 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
4552 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
4553 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
4554 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
4555 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
4556 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
4557 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
4558 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
4559 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
4560 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
4561 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
4562 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
4563 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
4564 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
4567 static struct TokenInfo system_setup_tokens[] =
4569 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
4570 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4573 static struct TokenInfo options_setup_tokens[] =
4575 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
4578 static char *get_corrected_login_name(char *login_name)
4580 /* needed because player name must be a fixed length string */
4581 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4583 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4584 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4586 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
4587 if (strchr(login_name_new, ' '))
4588 *strchr(login_name_new, ' ') = '\0';
4590 return login_name_new;
4593 static void setSetupInfoToDefaults(struct SetupInfo *si)
4597 si->player_name = get_corrected_login_name(getLoginName());
4600 si->sound_loops = TRUE;
4601 si->sound_music = TRUE;
4602 si->sound_simple = TRUE;
4604 si->double_buffering = TRUE;
4605 si->direct_draw = !si->double_buffering;
4606 si->scroll_delay = TRUE;
4607 si->soft_scrolling = TRUE;
4609 si->autorecord = TRUE;
4610 si->quick_doors = FALSE;
4611 si->team_mode = FALSE;
4612 si->handicap = TRUE;
4613 si->skip_levels = TRUE;
4614 si->time_limit = TRUE;
4615 si->fullscreen = FALSE;
4616 si->ask_on_escape = TRUE;
4618 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4619 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4620 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4621 si->override_level_graphics = FALSE;
4622 si->override_level_sounds = FALSE;
4623 si->override_level_music = FALSE;
4625 si->editor.el_boulderdash = TRUE;
4626 si->editor.el_emerald_mine = TRUE;
4627 si->editor.el_emerald_mine_club = TRUE;
4628 si->editor.el_more = TRUE;
4629 si->editor.el_sokoban = TRUE;
4630 si->editor.el_supaplex = TRUE;
4631 si->editor.el_diamond_caves = TRUE;
4632 si->editor.el_dx_boulderdash = TRUE;
4633 si->editor.el_chars = TRUE;
4634 si->editor.el_custom = TRUE;
4635 si->editor.el_custom_more = FALSE;
4637 si->editor.el_headlines = TRUE;
4638 si->editor.el_user_defined = FALSE;
4640 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4641 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4642 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4644 for (i = 0; i < MAX_PLAYERS; i++)
4646 si->input[i].use_joystick = FALSE;
4647 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4648 si->input[i].joy.xleft = JOYSTICK_XLEFT;
4649 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4650 si->input[i].joy.xright = JOYSTICK_XRIGHT;
4651 si->input[i].joy.yupper = JOYSTICK_YUPPER;
4652 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4653 si->input[i].joy.ylower = JOYSTICK_YLOWER;
4654 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
4655 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
4656 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
4657 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4658 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
4659 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
4660 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
4661 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
4664 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4665 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4667 si->options.verbose = FALSE;
4670 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4674 if (!setup_file_hash)
4679 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4680 setSetupInfo(global_setup_tokens, i,
4681 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4686 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4687 setSetupInfo(editor_setup_tokens, i,
4688 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4691 /* shortcut setup */
4692 ssi = setup.shortcut;
4693 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4694 setSetupInfo(shortcut_setup_tokens, i,
4695 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4696 setup.shortcut = ssi;
4699 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4703 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4705 sii = setup.input[pnr];
4706 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4708 char full_token[100];
4710 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4711 setSetupInfo(player_setup_tokens, i,
4712 getHashEntry(setup_file_hash, full_token));
4714 setup.input[pnr] = sii;
4719 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4720 setSetupInfo(system_setup_tokens, i,
4721 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4725 soi = setup.options;
4726 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4727 setSetupInfo(options_setup_tokens, i,
4728 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4729 setup.options = soi;
4734 char *filename = getSetupFilename();
4735 SetupFileHash *setup_file_hash = NULL;
4737 /* always start with reliable default values */
4738 setSetupInfoToDefaults(&setup);
4740 setup_file_hash = loadSetupFileHash(filename);
4742 if (setup_file_hash)
4744 char *player_name_new;
4746 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4747 decodeSetupFileHash(setup_file_hash);
4749 setup.direct_draw = !setup.double_buffering;
4751 freeSetupFileHash(setup_file_hash);
4753 /* needed to work around problems with fixed length strings */
4754 player_name_new = get_corrected_login_name(setup.player_name);
4755 free(setup.player_name);
4756 setup.player_name = player_name_new;
4759 Error(ERR_WARN, "using default setup values");
4764 char *filename = getSetupFilename();
4768 InitUserDataDirectory();
4770 if (!(file = fopen(filename, MODE_WRITE)))
4772 Error(ERR_WARN, "cannot write setup file '%s'", filename);
4776 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4777 getCookie("SETUP")));
4778 fprintf(file, "\n");
4782 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4784 /* just to make things nicer :) */
4785 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4786 i == SETUP_TOKEN_GRAPHICS_SET)
4787 fprintf(file, "\n");
4789 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4794 fprintf(file, "\n");
4795 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4796 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4798 /* shortcut setup */
4799 ssi = setup.shortcut;
4800 fprintf(file, "\n");
4801 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4802 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4805 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4809 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4810 fprintf(file, "\n");
4812 sii = setup.input[pnr];
4813 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4814 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4819 fprintf(file, "\n");
4820 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4821 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4824 soi = setup.options;
4825 fprintf(file, "\n");
4826 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4827 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4831 SetFilePermissions(filename, PERMS_PRIVATE);
4834 void LoadCustomElementDescriptions()
4836 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4837 SetupFileHash *setup_file_hash;
4840 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4842 if (element_info[i].custom_description != NULL)
4844 free(element_info[i].custom_description);
4845 element_info[i].custom_description = NULL;
4849 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4852 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4854 char *token = getStringCat2(element_info[i].token_name, ".name");
4855 char *value = getHashEntry(setup_file_hash, token);
4858 element_info[i].custom_description = getStringCopy(value);
4863 freeSetupFileHash(setup_file_hash);
4866 void LoadSpecialMenuDesignSettings()
4868 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4869 SetupFileHash *setup_file_hash;
4872 /* always start with reliable default values from default config */
4873 for (i = 0; image_config_vars[i].token != NULL; i++)
4874 for (j = 0; image_config[j].token != NULL; j++)
4875 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4876 *image_config_vars[i].value =
4877 get_auto_parameter_value(image_config_vars[i].token,
4878 image_config[j].value);
4880 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4883 /* special case: initialize with default values that may be overwritten */
4884 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4886 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4887 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4888 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4890 if (value_x != NULL)
4891 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4892 if (value_y != NULL)
4893 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4894 if (list_size != NULL)
4895 menu.list_size[i] = get_integer_from_string(list_size);
4898 /* read (and overwrite with) values that may be specified in config file */
4899 for (i = 0; image_config_vars[i].token != NULL; i++)
4901 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4904 *image_config_vars[i].value =
4905 get_auto_parameter_value(image_config_vars[i].token, value);
4908 freeSetupFileHash(setup_file_hash);
4911 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4913 char *filename = getEditorSetupFilename();
4914 SetupFileList *setup_file_list, *list;
4915 SetupFileHash *element_hash;
4916 int num_unknown_tokens = 0;
4919 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4922 element_hash = newSetupFileHash();
4924 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4925 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4927 /* determined size may be larger than needed (due to unknown elements) */
4929 for (list = setup_file_list; list != NULL; list = list->next)
4932 /* add space for up to 3 more elements for padding that may be needed */
4935 *elements = checked_malloc(*num_elements * sizeof(int));
4938 for (list = setup_file_list; list != NULL; list = list->next)
4940 char *value = getHashEntry(element_hash, list->token);
4942 if (value == NULL) /* try to find obsolete token mapping */
4944 char *mapped_token = get_mapped_token(list->token);
4946 if (mapped_token != NULL)
4948 value = getHashEntry(element_hash, mapped_token);
4956 (*elements)[(*num_elements)++] = atoi(value);
4960 if (num_unknown_tokens == 0)
4962 Error(ERR_RETURN_LINE, "-");
4963 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4964 Error(ERR_RETURN, "- config file: '%s'", filename);
4966 num_unknown_tokens++;
4969 Error(ERR_RETURN, "- token: '%s'", list->token);
4973 if (num_unknown_tokens > 0)
4974 Error(ERR_RETURN_LINE, "-");
4976 while (*num_elements % 4) /* pad with empty elements, if needed */
4977 (*elements)[(*num_elements)++] = EL_EMPTY;
4979 freeSetupFileList(setup_file_list);
4980 freeSetupFileHash(element_hash);
4983 for (i = 0; i < *num_elements; i++)
4984 printf("editor: element '%s' [%d]\n",
4985 element_info[(*elements)[i]].token_name, (*elements)[i]);
4989 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4992 SetupFileHash *setup_file_hash = NULL;
4993 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4994 char *filename_music, *filename_prefix, *filename_info;
5000 token_to_value_ptr[] =
5002 { "title_header", &tmp_music_file_info.title_header },
5003 { "artist_header", &tmp_music_file_info.artist_header },
5004 { "album_header", &tmp_music_file_info.album_header },
5005 { "year_header", &tmp_music_file_info.year_header },
5007 { "title", &tmp_music_file_info.title },
5008 { "artist", &tmp_music_file_info.artist },
5009 { "album", &tmp_music_file_info.album },
5010 { "year", &tmp_music_file_info.year },
5016 filename_music = (is_sound ? getCustomSoundFilename(basename) :
5017 getCustomMusicFilename(basename));
5019 if (filename_music == NULL)
5022 /* ---------- try to replace file extension ---------- */
5024 filename_prefix = getStringCopy(filename_music);
5025 if (strrchr(filename_prefix, '.') != NULL)
5026 *strrchr(filename_prefix, '.') = '\0';
5027 filename_info = getStringCat2(filename_prefix, ".txt");
5030 printf("trying to load file '%s'...\n", filename_info);
5033 if (fileExists(filename_info))
5034 setup_file_hash = loadSetupFileHash(filename_info);
5036 free(filename_prefix);
5037 free(filename_info);
5039 if (setup_file_hash == NULL)
5041 /* ---------- try to add file extension ---------- */
5043 filename_prefix = getStringCopy(filename_music);
5044 filename_info = getStringCat2(filename_prefix, ".txt");
5047 printf("trying to load file '%s'...\n", filename_info);
5050 if (fileExists(filename_info))
5051 setup_file_hash = loadSetupFileHash(filename_info);
5053 free(filename_prefix);
5054 free(filename_info);
5057 if (setup_file_hash == NULL)
5060 /* ---------- music file info found ---------- */
5062 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5064 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5066 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5068 *token_to_value_ptr[i].value_ptr =
5069 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5072 tmp_music_file_info.basename = getStringCopy(basename);
5073 tmp_music_file_info.music = music;
5074 tmp_music_file_info.is_sound = is_sound;
5076 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5077 *new_music_file_info = tmp_music_file_info;
5079 return new_music_file_info;
5082 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5084 return get_music_file_info_ext(basename, music, FALSE);
5087 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5089 return get_music_file_info_ext(basename, sound, TRUE);
5092 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5093 char *basename, boolean is_sound)
5095 for (; list != NULL; list = list->next)
5096 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5102 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5104 return music_info_listed_ext(list, basename, FALSE);
5107 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5109 return music_info_listed_ext(list, basename, TRUE);
5112 void LoadMusicInfo()
5114 char *music_directory = getCustomMusicDirectory();
5115 int num_music = getMusicListSize();
5116 int num_music_noconf = 0;
5117 int num_sounds = getSoundListSize();
5119 struct dirent *dir_entry;
5120 struct FileInfo *music, *sound;
5121 struct MusicFileInfo *next, **new;
5124 while (music_file_info != NULL)
5126 next = music_file_info->next;
5128 checked_free(music_file_info->basename);
5130 checked_free(music_file_info->title_header);
5131 checked_free(music_file_info->artist_header);
5132 checked_free(music_file_info->album_header);
5133 checked_free(music_file_info->year_header);
5135 checked_free(music_file_info->title);
5136 checked_free(music_file_info->artist);
5137 checked_free(music_file_info->album);
5138 checked_free(music_file_info->year);
5140 free(music_file_info);
5142 music_file_info = next;
5145 new = &music_file_info;
5147 for (i = 0; i < num_music; i++)
5149 music = getMusicListEntry(i);
5151 if (music->filename == NULL)
5154 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5157 /* a configured file may be not recognized as music */
5158 if (!FileIsMusic(music->filename))
5162 printf("::: -> '%s' (configured)\n", music->filename);
5165 if (!music_info_listed(music_file_info, music->filename))
5167 *new = get_music_file_info(music->filename, i);
5169 new = &(*new)->next;
5173 if ((dir = opendir(music_directory)) == NULL)
5175 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5179 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
5181 char *basename = dir_entry->d_name;
5182 boolean music_already_used = FALSE;
5185 /* skip all music files that are configured in music config file */
5186 for (i = 0; i < num_music; i++)
5188 music = getMusicListEntry(i);
5190 if (music->filename == NULL)
5193 if (strcmp(basename, music->filename) == 0)
5195 music_already_used = TRUE;
5200 if (music_already_used)
5203 if (!FileIsMusic(basename))
5207 printf("::: -> '%s' (found in directory)\n", basename);
5210 if (!music_info_listed(music_file_info, basename))
5212 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5214 new = &(*new)->next;
5222 for (i = 0; i < num_sounds; i++)
5224 sound = getSoundListEntry(i);
5226 if (sound->filename == NULL)
5229 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5232 /* a configured file may be not recognized as sound */
5233 if (!FileIsSound(sound->filename))
5237 printf("::: -> '%s' (configured)\n", sound->filename);
5240 if (!sound_info_listed(music_file_info, sound->filename))
5242 *new = get_sound_file_info(sound->filename, i);
5244 new = &(*new)->next;
5249 for (next = music_file_info; next != NULL; next = next->next)
5250 printf("::: title == '%s'\n", next->title);
5254 void add_helpanim_entry(int element, int action, int direction, int delay,
5255 int *num_list_entries)
5257 struct HelpAnimInfo *new_list_entry;
5258 (*num_list_entries)++;
5261 checked_realloc(helpanim_info,
5262 *num_list_entries * sizeof(struct HelpAnimInfo));
5263 new_list_entry = &helpanim_info[*num_list_entries - 1];
5265 new_list_entry->element = element;
5266 new_list_entry->action = action;
5267 new_list_entry->direction = direction;
5268 new_list_entry->delay = delay;
5271 void print_unknown_token(char *filename, char *token, int token_nr)
5275 Error(ERR_RETURN_LINE, "-");
5276 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5277 Error(ERR_RETURN, "- config file: '%s'", filename);
5280 Error(ERR_RETURN, "- token: '%s'", token);
5283 void print_unknown_token_end(int token_nr)
5286 Error(ERR_RETURN_LINE, "-");
5289 void LoadHelpAnimInfo()
5291 char *filename = getHelpAnimFilename();
5292 SetupFileList *setup_file_list = NULL, *list;
5293 SetupFileHash *element_hash, *action_hash, *direction_hash;
5294 int num_list_entries = 0;
5295 int num_unknown_tokens = 0;
5298 if (fileExists(filename))
5299 setup_file_list = loadSetupFileList(filename);
5301 if (setup_file_list == NULL)
5303 /* use reliable default values from static configuration */
5304 SetupFileList *insert_ptr;
5306 insert_ptr = setup_file_list =
5307 newSetupFileList(helpanim_config[0].token,
5308 helpanim_config[0].value);
5310 for (i = 1; helpanim_config[i].token; i++)
5311 insert_ptr = addListEntry(insert_ptr,
5312 helpanim_config[i].token,
5313 helpanim_config[i].value);
5316 element_hash = newSetupFileHash();
5317 action_hash = newSetupFileHash();
5318 direction_hash = newSetupFileHash();
5320 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5321 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5323 for (i = 0; i < NUM_ACTIONS; i++)
5324 setHashEntry(action_hash, element_action_info[i].suffix,
5325 i_to_a(element_action_info[i].value));
5327 /* do not store direction index (bit) here, but direction value! */
5328 for (i = 0; i < NUM_DIRECTIONS; i++)
5329 setHashEntry(direction_hash, element_direction_info[i].suffix,
5330 i_to_a(1 << element_direction_info[i].value));
5332 for (list = setup_file_list; list != NULL; list = list->next)
5334 char *element_token, *action_token, *direction_token;
5335 char *element_value, *action_value, *direction_value;
5336 int delay = atoi(list->value);
5338 if (strcmp(list->token, "end") == 0)
5340 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5345 /* first try to break element into element/action/direction parts;
5346 if this does not work, also accept combined "element[.act][.dir]"
5347 elements (like "dynamite.active"), which are unique elements */
5349 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
5351 element_value = getHashEntry(element_hash, list->token);
5352 if (element_value != NULL) /* element found */
5353 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5357 /* no further suffixes found -- this is not an element */
5358 print_unknown_token(filename, list->token, num_unknown_tokens++);
5364 /* token has format "<prefix>.<something>" */
5366 action_token = strchr(list->token, '.'); /* suffix may be action ... */
5367 direction_token = action_token; /* ... or direction */
5369 element_token = getStringCopy(list->token);
5370 *strchr(element_token, '.') = '\0';
5372 element_value = getHashEntry(element_hash, element_token);
5374 if (element_value == NULL) /* this is no element */
5376 element_value = getHashEntry(element_hash, list->token);
5377 if (element_value != NULL) /* combined element found */
5378 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5381 print_unknown_token(filename, list->token, num_unknown_tokens++);
5383 free(element_token);
5388 action_value = getHashEntry(action_hash, action_token);
5390 if (action_value != NULL) /* action found */
5392 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5395 free(element_token);
5400 direction_value = getHashEntry(direction_hash, direction_token);
5402 if (direction_value != NULL) /* direction found */
5404 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5407 free(element_token);
5412 if (strchr(action_token + 1, '.') == NULL)
5414 /* no further suffixes found -- this is not an action nor direction */
5416 element_value = getHashEntry(element_hash, list->token);
5417 if (element_value != NULL) /* combined element found */
5418 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5421 print_unknown_token(filename, list->token, num_unknown_tokens++);
5423 free(element_token);
5428 /* token has format "<prefix>.<suffix>.<something>" */
5430 direction_token = strchr(action_token + 1, '.');
5432 action_token = getStringCopy(action_token);
5433 *strchr(action_token + 1, '.') = '\0';
5435 action_value = getHashEntry(action_hash, action_token);
5437 if (action_value == NULL) /* this is no action */
5439 element_value = getHashEntry(element_hash, list->token);
5440 if (element_value != NULL) /* combined element found */
5441 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5444 print_unknown_token(filename, list->token, num_unknown_tokens++);
5446 free(element_token);
5452 direction_value = getHashEntry(direction_hash, direction_token);
5454 if (direction_value != NULL) /* direction found */
5456 add_helpanim_entry(atoi(element_value), atoi(action_value),
5457 atoi(direction_value), delay, &num_list_entries);
5459 free(element_token);
5465 /* this is no direction */
5467 element_value = getHashEntry(element_hash, list->token);
5468 if (element_value != NULL) /* combined element found */
5469 add_helpanim_entry(atoi(element_value), -1, -1, delay,
5472 print_unknown_token(filename, list->token, num_unknown_tokens++);
5474 free(element_token);
5478 print_unknown_token_end(num_unknown_tokens);
5480 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5481 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
5483 freeSetupFileList(setup_file_list);
5484 freeSetupFileHash(element_hash);
5485 freeSetupFileHash(action_hash);
5486 freeSetupFileHash(direction_hash);
5489 for (i = 0; i < num_list_entries; i++)
5490 printf("::: %d, %d, %d => %d\n",
5491 helpanim_info[i].element,
5492 helpanim_info[i].action,
5493 helpanim_info[i].direction,
5494 helpanim_info[i].delay);
5498 void LoadHelpTextInfo()
5500 char *filename = getHelpTextFilename();
5503 if (helptext_info != NULL)
5505 freeSetupFileHash(helptext_info);
5506 helptext_info = NULL;
5509 if (fileExists(filename))
5510 helptext_info = loadSetupFileHash(filename);
5512 if (helptext_info == NULL)
5514 /* use reliable default values from static configuration */
5515 helptext_info = newSetupFileHash();
5517 for (i = 0; helptext_config[i].token; i++)
5518 setHashEntry(helptext_info,
5519 helptext_config[i].token,
5520 helptext_config[i].value);
5524 BEGIN_HASH_ITERATION(helptext_info, itr)
5526 printf("::: '%s' => '%s'\n",
5527 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5529 END_HASH_ITERATION(hash, itr)
5534 /* ------------------------------------------------------------------------- *
5536 * ------------------------------------------------------------------------- */
5538 #define MAX_NUM_CONVERT_LEVELS 1000
5540 void ConvertLevels()
5542 static LevelDirTree *convert_leveldir = NULL;
5543 static int convert_level_nr = -1;
5544 static int num_levels_handled = 0;
5545 static int num_levels_converted = 0;
5546 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5549 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5550 global.convert_leveldir);
5552 if (convert_leveldir == NULL)
5553 Error(ERR_EXIT, "no such level identifier: '%s'",
5554 global.convert_leveldir);
5556 leveldir_current = convert_leveldir;
5558 if (global.convert_level_nr != -1)
5560 convert_leveldir->first_level = global.convert_level_nr;
5561 convert_leveldir->last_level = global.convert_level_nr;
5564 convert_level_nr = convert_leveldir->first_level;
5566 printf_line("=", 79);
5567 printf("Converting levels\n");
5568 printf_line("-", 79);
5569 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5570 printf("Level series name: '%s'\n", convert_leveldir->name);
5571 printf("Level series author: '%s'\n", convert_leveldir->author);
5572 printf("Number of levels: %d\n", convert_leveldir->levels);
5573 printf_line("=", 79);
5576 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5577 levels_failed[i] = FALSE;
5579 while (convert_level_nr <= convert_leveldir->last_level)
5581 char *level_filename;
5584 level_nr = convert_level_nr++;
5586 printf("Level %03d: ", level_nr);
5588 LoadLevel(level_nr);
5589 if (level.no_valid_file)
5591 printf("(no level)\n");
5595 printf("converting level ... ");
5597 level_filename = getDefaultLevelFilename(level_nr);
5598 new_level = !fileExists(level_filename);
5602 SaveLevel(level_nr);
5604 num_levels_converted++;
5606 printf("converted.\n");
5610 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5611 levels_failed[level_nr] = TRUE;
5613 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5616 num_levels_handled++;
5620 printf_line("=", 79);
5621 printf("Number of levels handled: %d\n", num_levels_handled);
5622 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5623 (num_levels_handled ?
5624 num_levels_converted * 100 / num_levels_handled : 0));
5625 printf_line("-", 79);
5626 printf("Summary (for automatic parsing by scripts):\n");
5627 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5628 convert_leveldir->identifier, num_levels_converted,
5630 (num_levels_handled ?
5631 num_levels_converted * 100 / num_levels_handled : 0));
5633 if (num_levels_handled != num_levels_converted)
5635 printf(", FAILED:");
5636 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5637 if (levels_failed[i])
5642 printf_line("=", 79);