X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=bf3b77e7d063e9662243e6afdd0dbeeeb2a19d69;hb=d42e0a36609c7990f8b9f6fcd80f37f65bcf5149;hp=1f98f641e2ea8bd38eda6ea27a935ed84c8d5bf0;hpb=5dd3c868d9df906623a5fc0ff2124655a14e3715;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 1f98f641..bf3b77e7 100644 --- a/src/files.c +++ b/src/files.c @@ -29,7 +29,7 @@ #define CHUNK_SIZE_NONE -1 /* do not write chunk size */ #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */ #define LEVEL_HEADER_SIZE 80 /* size of level file header */ -#define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */ +#define LEVEL_HEADER_UNUSED 7 /* unused level header bytes */ #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */ #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */ #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */ @@ -92,7 +92,10 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change) change->can_change = FALSE; change->events = CE_BITMASK_DEFAULT; - change->sides = CH_SIDE_ANY; + + change->trigger_player = CH_PLAYER_ANY; + change->trigger_side = CH_SIDE_ANY; + change->trigger_page = CH_PAGE_ANY; change->target_element = EL_EMPTY_SPACE; @@ -141,15 +144,27 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) level->time = 100; level->gems_needed = 0; + level->amoeba_speed = 10; + level->time_magic_wall = 10; level->time_wheel = 10; level->time_light = 10; level->time_timegate = 10; + level->amoeba_content = EL_DIAMOND; + level->double_speed = FALSE; level->initial_gravity = FALSE; level->em_slippery_gems = FALSE; + level->block_last_field = FALSE; + level->sp_block_last_field = TRUE; + + level->use_spring_bug = FALSE; + + level->can_move_into_acid_bits = ~0; /* everything can move into acid */ + + level->use_step_counter = FALSE; level->use_custom_template = FALSE; @@ -206,6 +221,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) if (IS_CUSTOM_ELEMENT(element)) { + element_info[element].access_direction = MV_ALL_DIRECTIONS; + element_info[element].collect_score = 10; /* special default */ element_info[element].collect_count = 1; /* special default */ @@ -215,7 +232,7 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].move_delay_random = 0; element_info[element].move_pattern = MV_ALL_DIRECTIONS; - element_info[element].move_direction_initial = MV_AUTOMATIC; + element_info[element].move_direction_initial = MV_START_AUTOMATIC; element_info[element].move_stepsize = TILEX / 8; element_info[element].move_enter_element = EL_EMPTY_SPACE; element_info[element].move_leave_element = EL_EMPTY_SPACE; @@ -223,12 +240,16 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].slippery_type = SLIPPERY_ANY_RANDOM; + element_info[element].explosion_delay = 18; + element_info[element].ignition_delay = 8; + for (x = 0; x < 3; x++) for (y = 0; y < 3; y++) element_info[element].content[x][y] = EL_EMPTY_SPACE; element_info[element].access_type = 0; element_info[element].access_layer = 0; + element_info[element].access_protected = 0; element_info[element].walk_to_action = 0; element_info[element].smash_targets = 0; element_info[element].deadliness = 0; @@ -244,6 +265,9 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) for (j = 0; j < NUM_EP_BITFIELDS; j++) Properties[element][j] = EP_BITMASK_DEFAULT; + /* now set default properties */ + SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE); + element_info[element].modified_settings = FALSE; } else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR) @@ -258,12 +282,14 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) /* default: only one element in group */ element_info[element].group->num_elements = 1; + + element_info[element].group->choice_mode = ANIM_RANDOM; } } BorderElement = EL_STEELWALL; - level->no_level_file = FALSE; + level->no_valid_file = FALSE; if (leveldir_current == NULL) /* only when dumping level */ return; @@ -304,6 +330,7 @@ static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info) level_file_info->nr = 0; level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN; level_file_info->packed = FALSE; + level_file_info->basename = NULL; level_file_info->filename = NULL; } @@ -325,13 +352,40 @@ static char *getLevelFilenameFromBasename(char *basename) return filename; } +static int getFileTypeFromBasename(char *basename) +{ + static char *filename = NULL; + struct stat file_status; + + /* ---------- try to determine file type from filename ---------- */ + + /* check for typical filename of a Supaplex level package file */ + if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 || + strncmp(basename, "LEVELS.D", 8) == 0)) + return LEVEL_FILE_TYPE_SP; + + /* ---------- try to determine file type from filesize ---------- */ + + checked_free(filename); + filename = getPath2(getCurrentLevelDir(), basename); + + if (stat(filename, &file_status) == 0) + { + /* check for typical filesize of a Supaplex level package file */ + if (file_status.st_size == 170496) + return LEVEL_FILE_TYPE_SP; + } + + return LEVEL_FILE_TYPE_UNKNOWN; +} + static char *getSingleLevelBasename(int nr, int type) { static char basename[MAX_FILENAME_LEN]; + char *level_filename = getStringCopy(leveldir_current->level_filename); - if (leveldir_current->level_filename == NULL) - leveldir_current->level_filename = - getStringCat2("%%03d.", LEVELFILE_EXTENSION); + if (level_filename == NULL) + level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION); switch (type) { @@ -348,10 +402,12 @@ static char *getSingleLevelBasename(int nr, int type) case LEVEL_FILE_TYPE_UNKNOWN: default: - sprintf(basename, leveldir_current->level_filename, nr); + sprintf(basename, level_filename, nr); break; } + free(level_filename); + return basename; } @@ -374,28 +430,18 @@ static char *getPackedLevelBasename(int type) while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */ { char *entry_basename = dir_entry->d_name; - boolean valid_entry_found = FALSE; + int entry_type = getFileTypeFromBasename(entry_basename); - switch (type) + if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */ { - case LEVEL_FILE_TYPE_SP: - if (strlen(entry_basename) == 10 && - (strncmp(entry_basename, "levels.d", 8) == 0 || - strncmp(entry_basename, "LEVELS.D", 8) == 0)) - { - /* looks like a typical filename of a Supaplex level package file */ - strcpy(basename, entry_basename); - valid_entry_found = TRUE; - } - break; + if (type == LEVEL_FILE_TYPE_UNKNOWN || + type == entry_type) + { + strcpy(basename, entry_basename); - default: - valid_entry_found = TRUE; break; + } } - - if (valid_entry_found) - break; } closedir(dir); @@ -408,23 +454,42 @@ static char *getSingleLevelFilename(int nr, int type) return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type)); } +#if 0 static char *getPackedLevelFilename(int type) { return getLevelFilenameFromBasename(getPackedLevelBasename(type)); } +#endif char *getDefaultLevelFilename(int nr) { return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND); } -static void setLevelFileInfo_Filename(struct LevelFileInfo *lfi) +static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi, + int type) +{ + lfi->type = type; + lfi->packed = FALSE; + lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type); + lfi->filename = getLevelFilenameFromBasename(lfi->basename); +} + +static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi, + int type) +{ + lfi->type = type; + lfi->packed = TRUE; + lfi->basename = getPackedLevelBasename(lfi->type); + lfi->filename = getLevelFilenameFromBasename(lfi->basename); +} + +static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) { /* special case: level number is negative => check for level template file */ if (lfi->nr < 0) { - lfi->type = LEVEL_FILE_TYPE_RND; - lfi->filename = getDefaultLevelFilename(lfi->nr); + setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND); return; } @@ -432,66 +497,34 @@ static void setLevelFileInfo_Filename(struct LevelFileInfo *lfi) if (leveldir_current->level_filename != NULL) { /* check for file name/pattern specified in "levelinfo.conf" */ - lfi->type = LEVEL_FILE_TYPE_UNKNOWN; - lfi->filename = getSingleLevelFilename(lfi->nr, lfi->type); + setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); if (fileExists(lfi->filename)) return; } /* check for native Rocks'n'Diamonds level file */ - lfi->type = LEVEL_FILE_TYPE_RND; - lfi->filename = getSingleLevelFilename(lfi->nr, lfi->type); + setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND); if (fileExists(lfi->filename)) return; /* check for classic Emerald Mine level file */ - lfi->type = LEVEL_FILE_TYPE_EM; - lfi->filename = getSingleLevelFilename(lfi->nr, lfi->type); + setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM); if (fileExists(lfi->filename)) return; - /* check for packed Supaplex level file */ - lfi->type = LEVEL_FILE_TYPE_SP; - lfi->filename = getPackedLevelFilename(lfi->type); + /* check for various packed level file formats */ + setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); if (fileExists(lfi->filename)) return; /* no known level file found -- try to use default values */ - lfi->type = LEVEL_FILE_TYPE_UNKNOWN; - lfi->filename = getSingleLevelFilename(lfi->nr, lfi->type); + setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); } -static void setLevelFileInfo_Filetype(struct LevelFileInfo *lfi) +static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi) { - struct stat file_status; - - if (lfi->type != LEVEL_FILE_TYPE_UNKNOWN) - return; - - /* ---------- try to determine file type from filename ---------- */ - - if (strlen(lfi->filename) == 10 && - (strncmp(lfi->filename, "levels.d", 8) == 0 || - strncmp(lfi->filename, "LEVELS.D", 8) == 0)) - { - /* looks like a typical filename of a Supaplex level package file */ - lfi->type = LEVEL_FILE_TYPE_SP; - - return; - } - - /* ---------- try to determine file type from file status ---------- */ - - if (stat(lfi->filename, &file_status) == 0) - { - if (file_status.st_size == 170496) - { - /* looks like a typical filesize of a Supaplex level package file */ - lfi->type = LEVEL_FILE_TYPE_SP; - - return; - } - } + if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN) + lfi->type = getFileTypeFromBasename(lfi->basename); } static struct LevelFileInfo *getLevelFileInfo(int nr) @@ -503,8 +536,8 @@ static struct LevelFileInfo *getLevelFileInfo(int nr) level_file_info.nr = nr; /* set requested level number */ - setLevelFileInfo_Filename(&level_file_info); - setLevelFileInfo_Filetype(&level_file_info); + determineLevelFileInfo_Filename(&level_file_info); + determineLevelFileInfo_Filetype(&level_file_info); return &level_file_info; } @@ -516,7 +549,7 @@ static struct LevelFileInfo *getLevelFileInfo(int nr) int getMappedElement(int element) { - /* map some (historic, now obsolete) elements */ + /* remap some (historic, now obsolete) elements */ #if 1 switch (element) @@ -557,7 +590,7 @@ int getMappedElement(int element) { Error(ERR_WARN, "invalid level element %d", element); - element = EL_CHAR_QUESTION; + element = EL_UNKNOWN; } break; } @@ -566,7 +599,7 @@ int getMappedElement(int element) { Error(ERR_WARN, "invalid level element %d", element); - element = EL_CHAR_QUESTION; + element = EL_UNKNOWN; } else if (element == EL_PLAYER_OBSOLETE) element = EL_PLAYER_1; @@ -577,6 +610,32 @@ int getMappedElement(int element) return element; } +int getMappedElementByVersion(int element, int game_version) +{ + /* remap some elements due to certain game version */ + + if (game_version <= VERSION_IDENT(2,2,0,0)) + { + /* map game font elements */ + element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT : + element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT : + element == EL_CHAR(']') ? EL_CHAR_UUMLAUT : + element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element); + } + + if (game_version < VERSION_IDENT(3,0,0,0)) + { + /* map Supaplex gravity tube elements */ + element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT : + element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT : + element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP : + element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN : + element); + } + + return element; +} + static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level) { level->file_version = getFileVersion(file); @@ -619,6 +678,15 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level) level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE); + level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE); + level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE); + + level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE); + + level->can_move_into_acid_bits = getFile16BitBE(file); + + level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE); + ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED); return chunk_size; @@ -973,6 +1041,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) ei->move_delay_fixed = getFile16BitBE(file); ei->move_delay_random = getFile16BitBE(file); + /* bits 0 - 15 of "move_pattern" ... */ ei->move_pattern = getFile16BitBE(file); ei->move_direction_initial = getFile8Bit(file); ei->move_stepsize = getFile8Bit(file); @@ -987,8 +1056,16 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) ei->move_leave_element = getMappedElement(getFile16BitBE(file)); ei->move_leave_type = getFile8Bit(file); + /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */ + ei->move_pattern |= (getFile16BitBE(file) << 16); + + ei->access_direction = getFile8Bit(file); + + ei->explosion_delay = getFile8Bit(file); + ei->ignition_delay = getFile8Bit(file); + /* some free bytes for future custom property values and padding */ - ReadUnusedBytesFromFile(file, 7); + ReadUnusedBytesFromFile(file, 2); /* read change property values */ @@ -1025,13 +1102,23 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) change->can_change = getFile8Bit(file); - change->sides = getFile8Bit(file); + change->trigger_side = getFile8Bit(file); - if (change->sides == CH_SIDE_NONE) /* correct empty sides field */ - change->sides = CH_SIDE_ANY; +#if 1 + change->trigger_player = getFile8Bit(file); + change->trigger_page = getFile8Bit(file); + + change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ? + CH_PAGE_ANY : (1 << change->trigger_page)); + + /* some free bytes for future change property values and padding */ + ReadUnusedBytesFromFile(file, 6); + +#else /* some free bytes for future change property values and padding */ ReadUnusedBytesFromFile(file, 8); +#endif } /* mark this custom element as modified */ @@ -1070,8 +1157,10 @@ static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level) ei->use_gfx_element = getFile8Bit(file); ei->gfx_element = getMappedElement(getFile16BitBE(file)); + group->choice_mode = getFile8Bit(file); + /* some free bytes for future values and padding */ - ReadUnusedBytesFromFile(file, 4); + ReadUnusedBytesFromFile(file, 3); for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++) group->element[i] = getMappedElement(getFile16BitBE(file)); @@ -1093,7 +1182,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level, if (!(file = fopen(filename, MODE_READ))) { - level->no_level_file = TRUE; + level->no_valid_file = TRUE; if (level != &level_template) Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename); @@ -1109,6 +1198,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level, getFileChunkBE(file, chunk_name, NULL); if (strcmp(chunk_name, "CAVE") != 0) { + level->no_valid_file = TRUE; + Error(ERR_WARN, "unknown format of level file '%s'", filename); fclose(file); return; @@ -1123,6 +1214,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level, if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL)) { + level->no_valid_file = TRUE; + Error(ERR_WARN, "unknown format of level file '%s'", filename); fclose(file); return; @@ -1130,6 +1223,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level, if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1) { + level->no_valid_file = TRUE; + Error(ERR_WARN, "unsupported version of level file '%s'", filename); fclose(file); return; @@ -1261,7 +1356,7 @@ static int map_em_element_yam(int element) default: Error(ERR_WARN, "invalid level element %d", element); - return EL_CHAR_QUESTION; + return EL_UNKNOWN; } } @@ -1275,10 +1370,13 @@ static int map_em_element_field(int element) switch (element) { case 0x00: return EL_ROCK; + case 0x01: return EL_ROCK; /* EMC */ case 0x02: return EL_DIAMOND; case 0x03: return EL_DIAMOND; case 0x04: return EL_ROBOT; case 0x05: return EL_ROBOT; /* EMC */ + case 0x06: return EL_EMPTY_SPACE; /* EMC */ + case 0x07: return EL_EMPTY_SPACE; /* EMC */ case 0x08: return EL_SPACESHIP_UP; case 0x09: return EL_SPACESHIP_RIGHT; case 0x0a: return EL_SPACESHIP_DOWN; @@ -1287,7 +1385,9 @@ static int map_em_element_field(int element) case 0x0d: return EL_SPACESHIP_RIGHT; case 0x0e: return EL_SPACESHIP_DOWN; case 0x0f: return EL_SPACESHIP_LEFT; + case 0x10: return EL_BOMB; + case 0x11: return EL_BOMB; /* EMC */ case 0x12: return EL_EMERALD; case 0x13: return EL_EMERALD; case 0x14: return EL_BUG_UP; @@ -1299,34 +1399,115 @@ static int map_em_element_field(int element) case 0x1a: return EL_BUG_DOWN; case 0x1b: return EL_BUG_LEFT; case 0x1c: return EL_AMOEBA_DROP; + case 0x1d: return EL_AMOEBA_DROP; /* EMC */ + case 0x1e: return EL_AMOEBA_DROP; /* EMC */ + case 0x1f: return EL_AMOEBA_DROP; /* EMC */ + case 0x20: return EL_ROCK; + case 0x21: return EL_BOMB; /* EMC */ + case 0x22: return EL_DIAMOND; /* EMC */ + case 0x23: return EL_EMERALD; /* EMC */ case 0x24: return EL_MAGIC_WALL; case 0x25: return EL_NUT; + case 0x26: return EL_NUT; /* EMC */ + case 0x27: return EL_NUT; /* EMC */ /* looks like magic wheel, but is _always_ activated */ case 0x28: return EL_ROBOT_WHEEL; /* EMC */ - case 0x29: return EL_YAMYAM; - case 0x2a: return EL_YAMYAM; - case 0x2b: return EL_YAMYAM; /* EMC */ - case 0x2c: return EL_YAMYAM; /* EMC */ + case 0x29: return EL_YAMYAM; /* up */ + case 0x2a: return EL_YAMYAM; /* down */ + case 0x2b: return EL_YAMYAM; /* left */ /* EMC */ + case 0x2c: return EL_YAMYAM; /* right */ /* EMC */ case 0x2d: return EL_QUICKSAND_FULL; + case 0x2e: return EL_EMPTY_SPACE; /* EMC */ + case 0x2f: return EL_EMPTY_SPACE; /* EMC */ + + case 0x30: return EL_EMPTY_SPACE; /* EMC */ + case 0x31: return EL_SAND; /* EMC */ + case 0x32: return EL_SAND; /* EMC */ + case 0x33: return EL_SAND; /* EMC */ + case 0x34: return EL_QUICKSAND_FULL; /* EMC */ + case 0x35: return EL_QUICKSAND_FULL; /* EMC */ + case 0x36: return EL_QUICKSAND_FULL; /* EMC */ + case 0x37: return EL_SAND; /* EMC */ + case 0x38: return EL_ROCK; /* EMC */ case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */ case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */ - case 0x3b: return EL_DYNAMITE_ACTIVE; - case 0x3c: return EL_DYNAMITE_ACTIVE; - case 0x3d: return EL_DYNAMITE_ACTIVE; - case 0x3e: return EL_DYNAMITE_ACTIVE; + case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */ + case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */ + case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */ + case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */ case 0x3f: return EL_ACID_POOL_BOTTOM; - case 0x40: return EL_EXIT_OPEN; - case 0x41: return EL_EXIT_OPEN; - case 0x42: return EL_EXIT_OPEN; - case 0x43: return EL_BALLOON; - case 0x4e: return EL_INVISIBLE_WALL; - case 0x65: return EL_ACID; /* EMC */ - case 0x73: return EL_SAND; /* EMC */ + + case 0x40: return EL_EXIT_OPEN; /* 1 */ + case 0x41: return EL_EXIT_OPEN; /* 2 */ + case 0x42: return EL_EXIT_OPEN; /* 3 */ + case 0x43: return EL_BALLOON; /* EMC */ + case 0x44: return EL_UNKNOWN; /* EMC ("plant") */ + case 0x45: return EL_SPRING; /* EMC */ + case 0x46: return EL_SPRING; /* falling */ /* EMC */ + case 0x47: return EL_SPRING; /* left */ /* EMC */ + case 0x48: return EL_SPRING; /* right */ /* EMC */ + case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */ + case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */ + case 0x4b: return EL_UNKNOWN; /* EMC ("android") */ + case 0x4c: return EL_EMPTY_SPACE; /* EMC */ + case 0x4d: return EL_UNKNOWN; /* EMC ("android") */ + case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */ + case 0x4f: return EL_UNKNOWN; /* EMC ("android") */ + + case 0x50: return EL_UNKNOWN; /* EMC ("android") */ + case 0x51: return EL_UNKNOWN; /* EMC ("android") */ + case 0x52: return EL_UNKNOWN; /* EMC ("android") */ + case 0x53: return EL_UNKNOWN; /* EMC ("android") */ + case 0x54: return EL_UNKNOWN; /* EMC ("android") */ + case 0x55: return EL_EMPTY_SPACE; /* EMC */ + case 0x56: return EL_EMPTY_SPACE; /* EMC */ + case 0x57: return EL_EMPTY_SPACE; /* EMC */ + case 0x58: return EL_EMPTY_SPACE; /* EMC */ + case 0x59: return EL_EMPTY_SPACE; /* EMC */ + case 0x5a: return EL_EMPTY_SPACE; /* EMC */ + case 0x5b: return EL_EMPTY_SPACE; /* EMC */ + case 0x5c: return EL_EMPTY_SPACE; /* EMC */ + case 0x5d: return EL_EMPTY_SPACE; /* EMC */ + case 0x5e: return EL_EMPTY_SPACE; /* EMC */ + case 0x5f: return EL_EMPTY_SPACE; /* EMC */ + + case 0x60: return EL_EMPTY_SPACE; /* EMC */ + case 0x61: return EL_EMPTY_SPACE; /* EMC */ + case 0x62: return EL_EMPTY_SPACE; /* EMC */ + case 0x63: return EL_SPRING; /* left */ /* EMC */ + case 0x64: return EL_SPRING; /* right */ /* EMC */ + case 0x65: return EL_ACID; /* 1 */ /* EMC */ + case 0x66: return EL_ACID; /* 2 */ /* EMC */ + case 0x67: return EL_ACID; /* 3 */ /* EMC */ + case 0x68: return EL_ACID; /* 4 */ /* EMC */ + case 0x69: return EL_ACID; /* 5 */ /* EMC */ + case 0x6a: return EL_ACID; /* 6 */ /* EMC */ + case 0x6b: return EL_ACID; /* 7 */ /* EMC */ + case 0x6c: return EL_ACID; /* 8 */ /* EMC */ + case 0x6d: return EL_EMPTY_SPACE; /* EMC */ + case 0x6e: return EL_EMPTY_SPACE; /* EMC */ + case 0x6f: return EL_EMPTY_SPACE; /* EMC */ + + case 0x70: return EL_EMPTY_SPACE; /* EMC */ + case 0x71: return EL_EMPTY_SPACE; /* EMC */ + case 0x72: return EL_NUT; /* left */ /* EMC */ + case 0x73: return EL_SAND; /* EMC (? "nut") */ case 0x74: return EL_STEELWALL; - case 0x7b: return EL_ACID; + case 0x75: return EL_EMPTY_SPACE; /* EMC */ + case 0x76: return EL_EMPTY_SPACE; /* EMC */ + case 0x77: return EL_BOMB; /* left */ /* EMC */ + case 0x78: return EL_BOMB; /* right */ /* EMC */ + case 0x79: return EL_ROCK; /* left */ /* EMC */ + case 0x7a: return EL_ROCK; /* right */ /* EMC */ + case 0x7b: return EL_ACID; /* (? EMC "blank") */ + case 0x7c: return EL_EMPTY_SPACE; /* EMC */ + case 0x7d: return EL_EMPTY_SPACE; /* EMC */ + case 0x7e: return EL_EMPTY_SPACE; /* EMC */ + case 0x7f: return EL_EMPTY_SPACE; /* EMC */ + case 0x80: return EL_EMPTY; case 0x81: return EL_WALL_SLIPPERY; case 0x82: return EL_SAND; @@ -1340,63 +1521,101 @@ static int map_em_element_field(int element) case 0x8a: return EL_EM_GATE_2; case 0x8b: return EL_EM_GATE_4; case 0x8c: return EL_EM_GATE_3; - case 0x8d: return EL_INVISIBLE_WALL; /* EMC */ + case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */ case 0x8e: return EL_EM_GATE_1_GRAY; case 0x8f: return EL_EM_GATE_2_GRAY; + case 0x90: return EL_EM_GATE_4_GRAY; case 0x91: return EL_EM_GATE_3_GRAY; case 0x92: return EL_MAGIC_WALL; - case 0x94: return EL_QUICKSAND_EMPTY; + case 0x93: return EL_ROBOT_WHEEL; + case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */ case 0x95: return EL_ACID_POOL_TOPLEFT; case 0x96: return EL_ACID_POOL_TOPRIGHT; case 0x97: return EL_ACID_POOL_BOTTOMLEFT; case 0x98: return EL_ACID_POOL_BOTTOMRIGHT; - case 0x99: return EL_ACID; - case 0x9a: return EL_AMOEBA_DEAD; - case 0x9b: return EL_AMOEBA_DEAD; - case 0x9c: return EL_AMOEBA_DEAD; - case 0x9d: return EL_AMOEBA_DEAD; + case 0x99: return EL_ACID; /* (? EMC "fake blank") */ + case 0x9a: return EL_AMOEBA_DEAD; /* 1 */ + case 0x9b: return EL_AMOEBA_DEAD; /* 2 */ + case 0x9c: return EL_AMOEBA_DEAD; /* 3 */ + case 0x9d: return EL_AMOEBA_DEAD; /* 4 */ case 0x9e: return EL_EXIT_CLOSED; - case 0x9f: return EL_CHAR_LESS; /* EMC */ - case 0x93: return EL_ROBOT_WHEEL; - - /* looks like normal dust, but behaves like wall */ - case 0xa0: return EL_WALL; /* EMC */ - - case 0xa8: return EL_EMC_WALL_1; /* EMC */ - case 0xa9: return EL_EMC_WALL_2; /* EMC */ - case 0xaa: return EL_EMC_WALL_3; /* EMC */ - case 0xab: return EL_EMC_WALL_7; /* EMC */ + case 0x9f: return EL_CHAR_LESS; /* arrow left */ + + /* looks like normal sand, but behaves like wall */ + case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */ + case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */ + case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */ + case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */ + case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */ + case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */ + case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */ + case 0xa7: return EL_EMPTY_SPACE; /* EMC */ + case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */ + case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */ + case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */ + case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */ + case 0xac: return EL_CHAR_COMMA; /* EMC */ + case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */ case 0xae: return EL_CHAR_MINUS; /* EMC */ case 0xaf: return EL_DYNAMITE; - case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */ - case 0xb1: return EL_EMC_WALL_8; /* EMC */ - - /* (exact steel wall) */ - case 0xb3: return EL_STEELWALL; /* EMC */ - case 0xb4: return EL_WALL_SLIPPERY; /* EMC */ - case 0xb5: return EL_EMC_WALL_6; /* EMC */ - case 0xb6: return EL_EMC_WALL_5; /* EMC */ - case 0xb7: return EL_EMC_WALL_4; /* EMC */ + case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */ + case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */ + case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */ + case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */ + case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */ + case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */ + case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */ + case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */ case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */ case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */ case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */ case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */ case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */ - case 0xbd: return EL_SAND; /* EMC */ + case 0xbd: return EL_SAND; /* EMC ("dirt") */ + case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */ + case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */ + + case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */ + case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */ + case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */ + case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */ + case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */ + case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */ + case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */ + case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */ + + /* characters: see above */ + case 0xec: return EL_CHAR_PERIOD; case 0xed: return EL_CHAR_EXCLAM; case 0xee: return EL_CHAR_COLON; case 0xef: return EL_CHAR_QUESTION; - case 0xf0: return EL_CHAR_GREATER; - case 0xf1: return EL_CHAR_COPYRIGHT; - case 0xfe: return EL_PLAYER_1; - case 0xff: return EL_PLAYER_2; + + case 0xf0: return EL_CHAR_GREATER; /* arrow right */ + case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */ + case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */ + case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */ + case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */ + case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */ + case 0xf6: return EL_EMPTY_SPACE; /* EMC */ + case 0xf7: return EL_EMPTY_SPACE; /* EMC */ + + case 0xf8: return EL_EMPTY_SPACE; /* EMC */ + case 0xf9: return EL_EMPTY_SPACE; /* EMC */ + case 0xfa: return EL_EMPTY_SPACE; /* EMC */ + case 0xfb: return EL_EMPTY_SPACE; /* EMC */ + case 0xfc: return EL_EMPTY_SPACE; /* EMC */ + case 0xfd: return EL_EMPTY_SPACE; /* EMC */ + + case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */ + case 0xff: return EL_PLAYER_2; /* EMC: "blank" */ default: + /* should never happen (all 8-bit value cases should be handled) */ Error(ERR_WARN, "invalid level element %d", element); - return EL_CHAR_QUESTION; + return EL_UNKNOWN; } } @@ -1419,7 +1638,7 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level, if (!(file = fopen(filename, MODE_READ))) { - level->no_level_file = TRUE; + level->no_valid_file = TRUE; Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename); @@ -1545,7 +1764,7 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level, Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y); Error(ERR_WARN, "invalid level element %d", element_old); - element_new = EL_CHAR_QUESTION; + element_new = EL_UNKNOWN; } level->field[x][y] = element_new; @@ -1611,7 +1830,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level, if (!(file = fopen(filename, MODE_READ))) { - level->no_level_file = TRUE; + level->no_valid_file = TRUE; Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename); @@ -1621,7 +1840,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level, /* position file stream to the requested level inside the level package */ if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0) { - level->no_level_file = TRUE; + level->no_valid_file = TRUE; Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename); @@ -1866,6 +2085,38 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */ if (level->game_version == VERSION_IDENT(2,0,1,0)) level->em_slippery_gems = TRUE; + + if (level->game_version < VERSION_IDENT(2,2,0,0)) + level->use_spring_bug = TRUE; + + if (level->game_version < VERSION_IDENT(3,0,9,0)) + { + int i, j; + + level->can_move_into_acid_bits = 0; /* nothing can move into acid */ + + setMoveIntoAcidProperty(level, EL_ROBOT, TRUE); + setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE); + setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE); + setMoveIntoAcidProperty(level, EL_BALLOON, TRUE); + + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE); + + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; + + for (j = 0; j < ei->num_change_pages; j++) + { + struct ElementChangeInfo *change = &ei->change_page[j]; + + change->trigger_player = CH_PLAYER_ANY; + change->trigger_page = CH_PAGE_ANY; + } + } + } } else { @@ -1911,7 +2162,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) { - int i, j; + int i, j, x, y; /* map custom element change events that have changed in newer versions (these following values were accidentally changed in version 3.0.1) */ @@ -1970,6 +2221,24 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } } + /* correct custom element values (for old levels without these options) */ + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; + + if (ei->access_direction == MV_NO_MOVING) + ei->access_direction = MV_ALL_DIRECTIONS; + + for (j = 0; j < ei->num_change_pages; j++) + { + struct ElementChangeInfo *change = &ei->change_page[j]; + + if (change->trigger_side == CH_SIDE_NONE) + change->trigger_side = CH_SIDE_ANY; + } + } + #if 0 /* set default push delay values (corrected since version 3.0.7-1) */ if (level->game_version < VERSION_IDENT(3,0,7,1)) @@ -1995,6 +2264,16 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } #endif + /* map elements that have changed in newer versions */ + level->amoeba_content = getMappedElementByVersion(level->amoeba_content, + level->game_version); + for (i = 0; i < MAX_ELEMENT_CONTENTS; i++) + for (x = 0; x < 3; x++) + for (y = 0; y < 3; y++) + level->yamyam_content[i][x][y] = + getMappedElementByVersion(level->yamyam_content[i][x][y], + level->game_version); + /* initialize element properties for level editor etc. */ InitElementPropertiesEngine(level->game_version); } @@ -2010,6 +2289,9 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename) { int element = level->field[x][y]; +#if 1 + element = getMappedElementByVersion(element, level->game_version); +#else if (level->game_version <= VERSION_IDENT(2,2,0,0)) { /* map game font elements */ @@ -2028,6 +2310,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename) element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN : element); } +#endif level->field[x][y] = element; } @@ -2129,6 +2412,15 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level) putFile8Bit(file, (level->use_custom_template ? 1 : 0)); + putFile8Bit(file, (level->block_last_field ? 1 : 0)); + putFile8Bit(file, (level->sp_block_last_field ? 1 : 0)); + + putFile8Bit(file, (level->use_spring_bug ? 1 : 0)); + + putFile16BitBE(file, level->can_move_into_acid_bits); + + putFile8Bit(file, (level->use_step_counter ? 1 : 0)); + WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED); } @@ -2411,7 +2703,8 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) putFile16BitBE(file, ei->move_delay_fixed); putFile16BitBE(file, ei->move_delay_random); - putFile16BitBE(file, ei->move_pattern); + /* bits 0 - 15 of "move_pattern" ... */ + putFile16BitBE(file, ei->move_pattern & 0xffff); putFile8Bit(file, ei->move_direction_initial); putFile8Bit(file, ei->move_stepsize); @@ -2425,8 +2718,16 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) putFile16BitBE(file, ei->move_leave_element); putFile8Bit(file, ei->move_leave_type); + /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */ + putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff); + + putFile8Bit(file, ei->access_direction); + + putFile8Bit(file, ei->explosion_delay); + putFile8Bit(file, ei->ignition_delay); + /* some free bytes for future custom property values and padding */ - WriteUnusedBytesToFile(file, 7); + WriteUnusedBytesToFile(file, 2); /* write change property values */ @@ -2458,10 +2759,21 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) putFile8Bit(file, change->can_change); - putFile8Bit(file, change->sides); + putFile8Bit(file, change->trigger_side); + +#if 1 + putFile8Bit(file, change->trigger_player); + putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE : + log_2(change->trigger_page))); + + /* some free bytes for future change property values and padding */ + WriteUnusedBytesToFile(file, 6); + +#else /* some free bytes for future change property values and padding */ WriteUnusedBytesToFile(file, 8); +#endif } } @@ -2481,8 +2793,10 @@ static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element) putFile8Bit(file, ei->use_gfx_element); putFile16BitBE(file, ei->gfx_element); + putFile8Bit(file, group->choice_mode); + /* some free bytes for future values and padding */ - WriteUnusedBytesToFile(file, 4); + WriteUnusedBytesToFile(file, 3); for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++) putFile16BitBE(file, group->element[i]); @@ -2620,29 +2934,40 @@ void SaveLevelTemplate() void DumpLevel(struct LevelInfo *level) { + if (level->no_valid_file) + { + Error(ERR_WARN, "cannot dump -- no valid level file found"); + + return; + } + printf_line("-", 79); printf("Level xxx (file version %08d, game version %08d)\n", level->file_version, level->game_version); printf_line("-", 79); - printf("Level Author: '%s'\n", level->author); - printf("Level Title: '%s'\n", level->name); + printf("Level author: '%s'\n", level->author); + printf("Level title: '%s'\n", level->name); printf("\n"); - printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy); + printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy); printf("\n"); - printf("Level Time: %d seconds\n", level->time); + printf("Level time: %d seconds\n", level->time); printf("Gems needed: %d\n", level->gems_needed); printf("\n"); - printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall); - printf("Time for Wheel: %d seconds\n", level->time_wheel); - printf("Time for Light: %d seconds\n", level->time_light); - printf("Time for Timegate: %d seconds\n", level->time_timegate); + printf("Time for magic wall: %d seconds\n", level->time_magic_wall); + printf("Time for wheel: %d seconds\n", level->time_wheel); + printf("Time for light: %d seconds\n", level->time_light); + printf("Time for timegate: %d seconds\n", level->time_timegate); printf("\n"); - printf("Amoeba Speed: %d\n", level->amoeba_speed); + printf("Amoeba speed: %d\n", level->amoeba_speed); printf("\n"); - printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no")); - printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no")); - printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no")); + printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no")); + printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no")); + printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no")); + printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no")); + printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no")); + printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no")); + printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no")); printf_line("-", 79); } @@ -2674,6 +2999,8 @@ static void setTapeInfoToDefaults() tape.recording = FALSE; tape.playing = FALSE; tape.pausing = FALSE; + + tape.no_valid_file = FALSE; } static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape) @@ -2838,7 +3165,13 @@ void LoadTapeFromFilename(char *filename) setTapeInfoToDefaults(); if (!(file = fopen(filename, MODE_READ))) + { + tape.no_valid_file = TRUE; + + Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename); + return; + } getFileChunkBE(file, chunk_name, NULL); if (strcmp(chunk_name, "RND1") == 0) @@ -2848,6 +3181,8 @@ void LoadTapeFromFilename(char *filename) getFileChunkBE(file, chunk_name, NULL); if (strcmp(chunk_name, "TAPE") != 0) { + tape.no_valid_file = TRUE; + Error(ERR_WARN, "unknown format of tape file '%s'", filename); fclose(file); return; @@ -2862,6 +3197,8 @@ void LoadTapeFromFilename(char *filename) if (!checkCookieString(cookie, TAPE_COOKIE_TMPL)) { + tape.no_valid_file = TRUE; + Error(ERR_WARN, "unknown format of tape file '%s'", filename); fclose(file); return; @@ -2869,6 +3206,8 @@ void LoadTapeFromFilename(char *filename) if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1) { + tape.no_valid_file = TRUE; + Error(ERR_WARN, "unsupported version of tape file '%s'", filename); fclose(file); return; @@ -3085,11 +3424,21 @@ void DumpTape(struct TapeInfo *tape) { int i, j; +#if 1 + if (tape->no_valid_file) + { + Error(ERR_WARN, "cannot dump -- no valid tape file found"); + + return; + } +#else if (TAPE_IS_EMPTY(*tape)) { Error(ERR_WARN, "no tape available for level %d", tape->level_nr); + return; } +#endif printf_line("-", 79); printf("Tape of Level %03d (file version %08d, game version %08d)\n",