From 29ea60b3d59414875c3bbc59e64f62d2e236ef2a Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Sun, 1 Nov 2009 17:38:05 +0100 Subject: [PATCH] rnd-20091101-2-src * added support for loading Supaplex levels in MPX level file format --- ChangeLog | 3 ++ src/conftime.h | 2 +- src/files.c | 22 +++++++++- src/game_sp/file.c | 95 +++++++++++++++++++++++++++++++++++++++----- src/game_sp/vb_lib.c | 3 +- src/libgame/misc.c | 20 ++++++++++ src/libgame/misc.h | 2 + 7 files changed, 132 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index a8395627..e42c6d05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2009-11-01 + * added support for loading Supaplex levels in MPX level file format + 2009-10-31 * fixed SP engine to set "game over" not before lead out counter done diff --git a/src/conftime.h b/src/conftime.h index 3cac76c2..3a907a71 100644 --- a/src/conftime.h +++ b/src/conftime.h @@ -1 +1 @@ -#define COMPILE_DATE_STRING "2009-10-31 23:44" +#define COMPILE_DATE_STRING "2009-11-01 17:22" diff --git a/src/files.c b/src/files.c index 05dfd5bd..329e4e93 100644 --- a/src/files.c +++ b/src/files.c @@ -1770,19 +1770,26 @@ static char *getLevelFilenameFromBasename(char *basename) static int getFileTypeFromBasename(char *basename) { + /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */ + 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 1 + if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d")) + return LEVEL_FILE_TYPE_SP; +#else if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 || strncmp(basename, "LEVELS.D", 8) == 0)) return LEVEL_FILE_TYPE_SP; +#endif /* check for typical filename of a Diamond Caves II level package file */ - if (strSuffix(basename, ".dc") || - strSuffix(basename, ".dc2")) + if (strSuffixLower(basename, ".dc") || + strSuffixLower(basename, ".dc2")) return LEVEL_FILE_TYPE_DC; /* ---------- try to determine file type from filesize ---------- */ @@ -1800,6 +1807,14 @@ static int getFileTypeFromBasename(char *basename) return LEVEL_FILE_TYPE_UNKNOWN; } +static boolean checkForPackageFromBasename(char *basename) +{ + /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!! + !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */ + + return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN); +} + static char *getSingleLevelBasename(int nr) { static char basename[MAX_FILENAME_LEN]; @@ -1953,6 +1968,9 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) setLevelFileInfo_FormatLevelFilename(lfi, filetype, leveldir_current->level_filename, nr); + + lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename); + if (fileExists(lfi->filename)) return; } diff --git a/src/game_sp/file.c b/src/game_sp/file.c index 2a657fcc..0d09e22e 100644 --- a/src/game_sp/file.c +++ b/src/game_sp/file.c @@ -149,7 +149,8 @@ void copyInternalEngineVars_SP() LevelLoaded = True; } -static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available) +static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, + boolean demo_available) { LevelInfoType *header = &native_sp_level.header; int i, x, y; @@ -157,8 +158,8 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available) /* for details of the Supaplex level format, see Herman Perk's Supaplex documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */ - native_sp_level.width = SP_PLAYFIELD_WIDTH; - native_sp_level.height = SP_PLAYFIELD_HEIGHT; + native_sp_level.width = width; + native_sp_level.height = height; /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */ for (y = 0; y < native_sp_level.height; y++) @@ -259,7 +260,7 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available) for (i = 0; i < SP_HEADER_SIZE; i++) native_sp_level.header_raw_bytes[i] = fgetc(file); - /* also load demo tape, if available */ + /* also load demo tape, if available (only in single level files) */ if (demo_available) { @@ -288,7 +289,7 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available) } } -boolean LoadNativeLevel_SP(char *filename, int pos) +boolean LoadNativeLevel_SP(char *filename, int level_pos) { FILE *file; int i, l, x, y; @@ -300,8 +301,13 @@ boolean LoadNativeLevel_SP(char *filename, int pos) boolean reading_multipart_level = FALSE; boolean use_empty_level = FALSE; LevelInfoType *header = &native_sp_level.header; - boolean demo_available = (strSuffix(filename, ".sp") || - strSuffix(filename, ".SP")); + boolean is_single_level_file = (strSuffixLower(filename, ".sp") || + strSuffixLower(filename, ".mpx")); + boolean demo_available = is_single_level_file; + boolean is_mpx_file = strSuffixLower(filename, ".mpx"); + int file_seek_pos = level_pos * SP_LEVEL_SIZE; + int level_width = SP_PLAYFIELD_WIDTH; + int level_height = SP_PLAYFIELD_HEIGHT; /* always start with reliable default values */ setLevelInfoToDefaults_SP(); @@ -309,13 +315,76 @@ boolean LoadNativeLevel_SP(char *filename, int pos) if (!(file = fopen(filename, MODE_READ))) { - Error(ERR_WARN, "cannot open level '%s' -- using empty level", filename); + Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename); return FALSE; } + if (is_mpx_file) + { + char mpx_chunk_name[4 + 1]; + int mpx_version; + int mpx_level_count; + LevelDescriptor *mpx_level_desc; + + getFileChunkBE(file, mpx_chunk_name, NULL); + + if (!strEqual(mpx_chunk_name, "MPX ")) + { + Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level", + filename); + + return FALSE; + } + + mpx_version = getFile16BitLE(file); + + if (mpx_version != 1) + { + Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level", + filename); + + return FALSE; + } + + mpx_level_count = getFile16BitLE(file); + + if (mpx_level_count < 1) + { + Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level", + filename); + + return FALSE; + } + + if (level_pos >= mpx_level_count) + { + Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level", + filename); + + return FALSE; + } + + mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor)); + + for (i = 0; i < mpx_level_count; i++) + { + LevelDescriptor *ldesc = &mpx_level_desc[i]; + + ldesc->Width = getFile16BitLE(file); + ldesc->Height = getFile16BitLE(file); + ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */ + ldesc->Size = getFile32BitLE(file); + } + + level_width = mpx_level_desc[level_pos].Width; + level_height = mpx_level_desc[level_pos].Height; + + file_seek_pos = mpx_level_desc[level_pos].OffSet - 1; + } + /* position file stream to the requested level (in case of level package) */ - if (fseek(file, pos * SP_LEVEL_SIZE, SEEK_SET) != 0) + if (fseek(file, file_seek_pos, SEEK_SET) != 0) { Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename); @@ -330,12 +399,16 @@ boolean LoadNativeLevel_SP(char *filename, int pos) of the level name, the multi-part level consists of only horizontal or vertical parts */ - for (l = pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++) + for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++) { - LoadNativeLevelFromFileStream_SP(file, demo_available); + LoadNativeLevelFromFileStream_SP(file, level_width, level_height, + demo_available); /* check if this level is a part of a bigger multi-part level */ + if (is_single_level_file) + break; + name_first = header->LevelTitle[0]; name_last = header->LevelTitle[SP_LEVEL_NAME_LEN - 1]; diff --git a/src/game_sp/vb_lib.c b/src/game_sp/vb_lib.c index 5c1dae75..c2a9fcee 100644 --- a/src/game_sp/vb_lib.c +++ b/src/game_sp/vb_lib.c @@ -91,7 +91,8 @@ boolean STRING_IS_LIKE(char *a, char *b) void FILE_GET(FILE *file, int offset, void *buffer, int num_bytes) { - fseek(file, offset - 1, SEEK_SET); + if (offset != -1) + fseek(file, offset - 1, SEEK_SET); while (num_bytes--) *(byte *)buffer++ = fgetc(file); diff --git a/src/libgame/misc.c b/src/libgame/misc.c index 5e4b9310..7883f325 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -637,6 +637,26 @@ boolean strSuffix(char *s, char *suffix) strncmp(&s[strlen(s) - strlen(suffix)], suffix, strlen(suffix)) == 0); } +boolean strPrefixLower(char *s, char *prefix) +{ + char *s_lower = getStringToLower(s); + boolean match = strPrefix(s_lower, prefix); + + free(s_lower); + + return match; +} + +boolean strSuffixLower(char *s, char *suffix) +{ + char *s_lower = getStringToLower(s); + boolean match = strSuffix(s_lower, suffix); + + free(s_lower); + + return match; +} + /* ------------------------------------------------------------------------- */ /* command line option handling functions */ diff --git a/src/libgame/misc.h b/src/libgame/misc.h index 56467dc1..6ac3d994 100644 --- a/src/libgame/misc.h +++ b/src/libgame/misc.h @@ -110,6 +110,8 @@ boolean strEqual(char *, char *); boolean strEqualN(char *, char *, int); boolean strPrefix(char *, char *); boolean strSuffix(char *, char *); +boolean strPrefixLower(char *, char *); +boolean strSuffixLower(char *, char *); void GetOptions(char **, void (*print_usage_function)(void)); -- 2.34.1