From 3a27dd9310ea41a2b6024a79c9538a0c0ae221a3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 30 Mar 2003 18:59:54 +0000 Subject: [PATCH] Descent I/II HOG and MVL archive support (thanks, Bradley Bell!). --- Makefile.am.newautomake | 2 + acconfig.h | 2 + archivers/.cvsignore | 2 + archivers/Makefile.am | 2 + archivers/hog.c | 574 ++++++++++++++++++++++++++++++++++++++++ archivers/mvl.c | 532 +++++++++++++++++++++++++++++++++++++ configure.in | 18 ++ physfs.c | 26 ++ physfs.h | 2 + physfs_internal.h | 16 ++ 10 files changed, 1176 insertions(+) create mode 100644 archivers/hog.c create mode 100644 archivers/mvl.c diff --git a/Makefile.am.newautomake b/Makefile.am.newautomake index dd14a32c..843dd416 100644 --- a/Makefile.am.newautomake +++ b/Makefile.am.newautomake @@ -48,6 +48,8 @@ libphysfs_la_SOURCES = \ physfs_byteorder.c \ archivers/dir.c \ archivers/grp.c \ + archivers/hog.c \ + archivers/mvl.c \ archivers/zip.c \ archivers/qpak.c \ platform/unix.c \ diff --git a/acconfig.h b/acconfig.h index 6ae4eab3..cc4b0221 100644 --- a/acconfig.h +++ b/acconfig.h @@ -4,6 +4,8 @@ #undef NDEBUG #undef PHYSFS_SUPPORTS_ZIP #undef PHYSFS_SUPPORTS_GRP +#undef PHYSFS_SUPPORTS_HOG +#undef PHYSFS_SUPPORTS_MVL #undef PHYSFS_HAVE_READLINE #undef PHYSFS_HAVE_LLSEEK diff --git a/archivers/.cvsignore b/archivers/.cvsignore index b9b844eb..fda24e20 100644 --- a/archivers/.cvsignore +++ b/archivers/.cvsignore @@ -5,6 +5,8 @@ unzip.lo dir.lo Makefile.in grp.lo +hog.lo +mvl.lo zip.lo libarchivers.la qpak.lo diff --git a/archivers/Makefile.am b/archivers/Makefile.am index 2eed0211..db857b96 100644 --- a/archivers/Makefile.am +++ b/archivers/Makefile.am @@ -9,6 +9,8 @@ endif libarchivers_la_SOURCES = \ dir.c \ grp.c \ + hog.c \ + mvl.c \ zip.c \ qpak.c diff --git a/archivers/hog.c b/archivers/hog.c new file mode 100644 index 00000000..123b9db9 --- /dev/null +++ b/archivers/hog.c @@ -0,0 +1,574 @@ +/* + * HOG support routines for PhysicsFS. + * + * This driver handles Descent I/II HOG archives. + * + * The format is very simple: + * + * The file always starts with the 3-byte signature "DHF" (Descent + * HOG file). After that the files of a HOG are just attached after + * another, divided by a 17 bytes header, which specifies the name + * and length (in bytes) of the forthcoming file! So you just read + * the header with its information of how big the following file is, + * and then skip exact that number of bytes to get to the next file + * in that HOG. + * + * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * char data[file_size]; // The file data + * } FILE_STRUCT; // Repeated until the end of the file. + * + * (That info is from http://www.descent2.com/ddn/specs/hog/) + * + * Please see the file LICENSE in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if (defined PHYSFS_SUPPORTS_HOG) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* + * One HOGentry is kept for each file in an open HOG archive. + */ +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} HOGentry; + +/* + * One HOGinfo is kept for each open HOG archive. + */ +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + HOGentry *entries; +} HOGinfo; + +/* + * One HOGfileinfo is kept for each open file in a HOG archive. + */ +typedef struct +{ + void *handle; + HOGentry *entry; + PHYSFS_uint32 curPos; +} HOGfileinfo; + + +static void HOG_dirClose(DirHandle *h); +static PHYSFS_sint64 HOG_read(FileHandle *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); +static PHYSFS_sint64 HOG_write(FileHandle *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); +static int HOG_eof(FileHandle *handle); +static PHYSFS_sint64 HOG_tell(FileHandle *handle); +static int HOG_seek(FileHandle *handle, PHYSFS_uint64 offset); +static PHYSFS_sint64 HOG_fileLength(FileHandle *handle); +static int HOG_fileClose(FileHandle *handle); +static int HOG_isArchive(const char *filename, int forWriting); +static DirHandle *HOG_openArchive(const char *name, int forWriting); +static LinkedStringList *HOG_enumerateFiles(DirHandle *h, + const char *dirname, + int omitSymLinks); +static int HOG_exists(DirHandle *h, const char *name); +static int HOG_isDirectory(DirHandle *h, const char *name, int *fileExists); +static int HOG_isSymLink(DirHandle *h, const char *name, int *fileExists); +static PHYSFS_sint64 HOG_getLastModTime(DirHandle *h, const char *n, int *e); +static FileHandle *HOG_openRead(DirHandle *h, const char *name, int *exist); +static FileHandle *HOG_openWrite(DirHandle *h, const char *name); +static FileHandle *HOG_openAppend(DirHandle *h, const char *name); +static int HOG_remove(DirHandle *h, const char *name); +static int HOG_mkdir(DirHandle *h, const char *name); + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG = +{ + "HOG", + HOG_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +static const FileFunctions __PHYSFS_FileFunctions_HOG = +{ + HOG_read, /* read() method */ + HOG_write, /* write() method */ + HOG_eof, /* eof() method */ + HOG_tell, /* tell() method */ + HOG_seek, /* seek() method */ + HOG_fileLength, /* fileLength() method */ + HOG_fileClose /* fileClose() method */ +}; + + +const DirFunctions __PHYSFS_DirFunctions_HOG = +{ + &__PHYSFS_ArchiveInfo_HOG, + HOG_isArchive, /* isArchive() method */ + HOG_openArchive, /* openArchive() method */ + HOG_enumerateFiles, /* enumerateFiles() method */ + HOG_exists, /* exists() method */ + HOG_isDirectory, /* isDirectory() method */ + HOG_isSymLink, /* isSymLink() method */ + HOG_getLastModTime, /* getLastModTime() method */ + HOG_openRead, /* openRead() method */ + HOG_openWrite, /* openWrite() method */ + HOG_openAppend, /* openAppend() method */ + HOG_remove, /* remove() method */ + HOG_mkdir, /* mkdir() method */ + HOG_dirClose /* dirClose() method */ +}; + + + +static void HOG_dirClose(DirHandle *h) +{ + HOGinfo *info = ((HOGinfo *) h->opaque); + free(info->filename); + free(info->entries); + free(info); + free(h); +} /* HOG_dirClose */ + + +static PHYSFS_sint64 HOG_read(FileHandle *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + HOGfileinfo *finfo = (HOGfileinfo *) (handle->opaque); + HOGentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* HOG_read */ + + +static PHYSFS_sint64 HOG_write(FileHandle *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* HOG_write */ + + +static int HOG_eof(FileHandle *handle) +{ + HOGfileinfo *finfo = (HOGfileinfo *) (handle->opaque); + HOGentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* HOG_eof */ + + +static PHYSFS_sint64 HOG_tell(FileHandle *handle) +{ + return(((HOGfileinfo *) (handle->opaque))->curPos); +} /* HOG_tell */ + + +static int HOG_seek(FileHandle *handle, PHYSFS_uint64 offset) +{ + HOGfileinfo *finfo = (HOGfileinfo *) (handle->opaque); + HOGentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* HOG_seek */ + + +static PHYSFS_sint64 HOG_fileLength(FileHandle *handle) +{ + HOGfileinfo *finfo = ((HOGfileinfo *) handle->opaque); + return((PHYSFS_sint64) finfo->entry->size); +} /* HOG_fileLength */ + + +static int HOG_fileClose(FileHandle *handle) +{ + HOGfileinfo *finfo = ((HOGfileinfo *) handle->opaque); + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + free(finfo); + free(handle); + return(1); +} /* HOG_fileClose */ + + +static int hog_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[13]; + PHYSFS_uint32 size; + PHYSFS_sint64 pos; + + *count = 0; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1) + goto openHog_failed; + + if (memcmp(buf, "DHF", 3) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openHog_failed; + } /* if */ + + while( 1 ) + { + if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1) + break; //eof here is ok + + if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1) + goto openHog_failed; + + size = PHYSFS_swapULE32(size); + + (*count)++; + + // Skip over entry + pos = __PHYSFS_platformTell(*fh); + if (pos == -1) + goto openHog_failed; + if (!__PHYSFS_platformSeek(*fh, pos + size)) + goto openHog_failed; + } + + // Rewind to start of entries + if (!__PHYSFS_platformSeek(*fh, 3)) + goto openHog_failed; + + return(1); + +openHog_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* hog_open */ + + +static int HOG_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = hog_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* HOG_isArchive */ + + +static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + HOGentry *a = (HOGentry *) _a; + return(strcmp(a[one].name, a[two].name)); +} /* hog_entry_cmp */ + + +static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + HOGentry tmp; + HOGentry *first = &(((HOGentry *) _a)[one]); + HOGentry *second = &(((HOGentry *) _a)[two]); + memcpy(&tmp, first, sizeof (HOGentry)); + memcpy(first, second, sizeof (HOGentry)); + memcpy(second, &tmp, sizeof (HOGentry)); +} /* hog_entry_swap */ + + +static int hog_load_entries(const char *name, int forWriting, HOGinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + HOGentry *entry; + char *ptr; + + BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (HOGentry *) malloc(sizeof (HOGentry) * fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = __PHYSFS_platformTell(fh); + if (entry->startPos == -1) + { + __PHYSFS_platformClose(fh); + return(0); + } + + // Skip over entry + if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size)) + { + __PHYSFS_platformClose(fh); + return(0); + } + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + hog_entry_cmp, hog_entry_swap); + return(1); +} /* hog_load_entries */ + + +static DirHandle *HOG_openArchive(const char *name, int forWriting) +{ + HOGinfo *info; + DirHandle *retval = malloc(sizeof (DirHandle)); + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + info = retval->opaque = malloc(sizeof (HOGinfo)); + if (info == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto HOG_openArchive_failed; + } /* if */ + + memset(info, '\0', sizeof (HOGinfo)); + + info->filename = (char *) malloc(strlen(name) + 1); + if (info->filename == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto HOG_openArchive_failed; + } /* if */ + + if (!hog_load_entries(name, forWriting, info)) + goto HOG_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + retval->funcs = &__PHYSFS_DirFunctions_HOG; + return(retval); + +HOG_openArchive_failed: + if (retval != NULL) + { + if (retval->opaque != NULL) + { + if (info->filename != NULL) + free(info->filename); + if (info->entries != NULL) + free(info->entries); + free(info); + } /* if */ + free(retval); + } /* if */ + + return(NULL); +} /* HOG_openArchive */ + + +static LinkedStringList *HOG_enumerateFiles(DirHandle *h, + const char *dirname, + int omitSymLinks) +{ + HOGinfo *info = ((HOGinfo *) h->opaque); + HOGentry *entry = info->entries; + LinkedStringList *retval = NULL, *p = NULL; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + /* no directories in HOG files. */ + BAIL_IF_MACRO(*dirname != '\0', ERR_NOT_A_DIR, NULL); + + for (i = 0; i < max; i++, entry++) + retval = __PHYSFS_addToLinkedStringList(retval, &p, entry->name, -1); + + return(retval); +} /* HOG_enumerateFiles */ + + +static HOGentry *hog_find_entry(HOGinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + HOGentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_platformStricmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* hog_find_entry */ + + +static int HOG_exists(DirHandle *h, const char *name) +{ + return(hog_find_entry(((HOGinfo *) h->opaque), name) != NULL); +} /* HOG_exists */ + + +static int HOG_isDirectory(DirHandle *h, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(h, name); + return(0); /* never directories in a groupfile. */ +} /* HOG_isDirectory */ + + +static int HOG_isSymLink(DirHandle *h, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(h, name); + return(0); /* never symlinks in a groupfile. */ +} /* HOG_isSymLink */ + + +static PHYSFS_sint64 HOG_getLastModTime(DirHandle *h, + const char *name, + int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) h->opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (hog_find_entry(info, name) != NULL); + if (*fileExists) /* use time of HOG itself in the physical filesystem. */ + retval = ((HOGinfo *) h->opaque)->last_mod_time; + + return(retval); +} /* HOG_getLastModTime */ + + +static FileHandle *HOG_openRead(DirHandle *h, const char *fnm, int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) h->opaque); + FileHandle *retval; + HOGfileinfo *finfo; + HOGentry *entry; + + entry = hog_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + retval = (FileHandle *) malloc(sizeof (FileHandle)); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + finfo = (HOGfileinfo *) malloc(sizeof (HOGfileinfo)); + if (finfo == NULL) + { + free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + free(finfo); + free(retval); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + retval->opaque = (void *) finfo; + retval->funcs = &__PHYSFS_FileFunctions_HOG; + retval->dirHandle = h; + return(retval); +} /* HOG_openRead */ + + +static FileHandle *HOG_openWrite(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openWrite */ + + +static FileHandle *HOG_openAppend(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openAppend */ + + +static int HOG_remove(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_remove */ + + +static int HOG_mkdir(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_mkdir */ + +#endif /* defined PHYSFS_SUPPORTS_HOG */ + +/* end of hog.c ... */ + diff --git a/archivers/mvl.c b/archivers/mvl.c new file mode 100644 index 00000000..c9ea828c --- /dev/null +++ b/archivers/mvl.c @@ -0,0 +1,532 @@ +/* + * MVL support routines for PhysicsFS. + * + * This driver handles Descent II Movielib archives. + * + * The file format of MVL is quite easy... + * + * //MVL File format - Written by Heiko Herrmann + * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library + * + * int num_files; // the number of files in this MVL + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * }DIR_STRUCT[num_files]; + * + * struct { + * char data[file_size]; // The file data + * }FILE_STRUCT[num_files]; + * + * (That info is from http://www.descent2.com/ddn/specs/mvl/) + * + * Please see the file LICENSE in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if (defined PHYSFS_SUPPORTS_MVL) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} MVLentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + MVLentry *entries; +} MVLinfo; + +typedef struct +{ + void *handle; + MVLentry *entry; + PHYSFS_uint32 curPos; +} MVLfileinfo; + + +static void MVL_dirClose(DirHandle *h); +static PHYSFS_sint64 MVL_read(FileHandle *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); +static PHYSFS_sint64 MVL_write(FileHandle *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); +static int MVL_eof(FileHandle *handle); +static PHYSFS_sint64 MVL_tell(FileHandle *handle); +static int MVL_seek(FileHandle *handle, PHYSFS_uint64 offset); +static PHYSFS_sint64 MVL_fileLength(FileHandle *handle); +static int MVL_fileClose(FileHandle *handle); +static int MVL_isArchive(const char *filename, int forWriting); +static DirHandle *MVL_openArchive(const char *name, int forWriting); +static LinkedStringList *MVL_enumerateFiles(DirHandle *h, + const char *dirname, + int omitSymLinks); +static int MVL_exists(DirHandle *h, const char *name); +static int MVL_isDirectory(DirHandle *h, const char *name, int *fileExists); +static int MVL_isSymLink(DirHandle *h, const char *name, int *fileExists); +static PHYSFS_sint64 MVL_getLastModTime(DirHandle *h, const char *n, int *e); +static FileHandle *MVL_openRead(DirHandle *h, const char *name, int *exist); +static FileHandle *MVL_openWrite(DirHandle *h, const char *name); +static FileHandle *MVL_openAppend(DirHandle *h, const char *name); +static int MVL_remove(DirHandle *h, const char *name); +static int MVL_mkdir(DirHandle *h, const char *name); + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL = +{ + "MVL", + MVL_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +static const FileFunctions __PHYSFS_FileFunctions_MVL = +{ + MVL_read, /* read() method */ + MVL_write, /* write() method */ + MVL_eof, /* eof() method */ + MVL_tell, /* tell() method */ + MVL_seek, /* seek() method */ + MVL_fileLength, /* fileLength() method */ + MVL_fileClose /* fileClose() method */ +}; + + +const DirFunctions __PHYSFS_DirFunctions_MVL = +{ + &__PHYSFS_ArchiveInfo_MVL, + MVL_isArchive, /* isArchive() method */ + MVL_openArchive, /* openArchive() method */ + MVL_enumerateFiles, /* enumerateFiles() method */ + MVL_exists, /* exists() method */ + MVL_isDirectory, /* isDirectory() method */ + MVL_isSymLink, /* isSymLink() method */ + MVL_getLastModTime, /* getLastModTime() method */ + MVL_openRead, /* openRead() method */ + MVL_openWrite, /* openWrite() method */ + MVL_openAppend, /* openAppend() method */ + MVL_remove, /* remove() method */ + MVL_mkdir, /* mkdir() method */ + MVL_dirClose /* dirClose() method */ +}; + + + +static void MVL_dirClose(DirHandle *h) +{ + MVLinfo *info = ((MVLinfo *) h->opaque); + free(info->filename); + free(info->entries); + free(info); + free(h); +} /* MVL_dirClose */ + + +static PHYSFS_sint64 MVL_read(FileHandle *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque); + MVLentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* MVL_read */ + + +static PHYSFS_sint64 MVL_write(FileHandle *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* MVL_write */ + + +static int MVL_eof(FileHandle *handle) +{ + MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque); + MVLentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* MVL_eof */ + + +static PHYSFS_sint64 MVL_tell(FileHandle *handle) +{ + return(((MVLfileinfo *) (handle->opaque))->curPos); +} /* MVL_tell */ + + +static int MVL_seek(FileHandle *handle, PHYSFS_uint64 offset) +{ + MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque); + MVLentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* MVL_seek */ + + +static PHYSFS_sint64 MVL_fileLength(FileHandle *handle) +{ + MVLfileinfo *finfo = ((MVLfileinfo *) handle->opaque); + return((PHYSFS_sint64) finfo->entry->size); +} /* MVL_fileLength */ + + +static int MVL_fileClose(FileHandle *handle) +{ + MVLfileinfo *finfo = ((MVLfileinfo *) handle->opaque); + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + free(finfo); + free(handle); + return(1); +} /* MVL_fileClose */ + + +static int mvl_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[4]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1) + goto openMvl_failed; + + if (memcmp(buf, "DMVL", 4) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openMvl_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openMvl_failed; + + *count = PHYSFS_swapULE32(*count); + + return(1); + +openMvl_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* mvl_open */ + + +static int MVL_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = mvl_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* MVL_isArchive */ + + +static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + MVLentry *a = (MVLentry *) _a; + return(strcmp(a[one].name, a[two].name)); +} /* mvl_entry_cmp */ + + +static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + MVLentry tmp; + MVLentry *first = &(((MVLentry *) _a)[one]); + MVLentry *second = &(((MVLentry *) _a)[two]); + memcpy(&tmp, first, sizeof (MVLentry)); + memcpy(first, second, sizeof (MVLentry)); + memcpy(second, &tmp, sizeof (MVLentry)); +} /* mvl_entry_swap */ + + +static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 location = 8; /* sizeof sig. */ + MVLentry *entry; + char *ptr; + + BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (MVLentry *) malloc(sizeof (MVLentry) * fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + location += (17 * fileCount); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = location; + location += entry->size; + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + mvl_entry_cmp, mvl_entry_swap); + return(1); +} /* mvl_load_entries */ + + +static DirHandle *MVL_openArchive(const char *name, int forWriting) +{ + MVLinfo *info; + DirHandle *retval = malloc(sizeof (DirHandle)); + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + info = retval->opaque = malloc(sizeof (MVLinfo)); + if (info == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto MVL_openArchive_failed; + } /* if */ + + memset(info, '\0', sizeof (MVLinfo)); + + info->filename = (char *) malloc(strlen(name) + 1); + if (info->filename == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto MVL_openArchive_failed; + } /* if */ + + if (!mvl_load_entries(name, forWriting, info)) + goto MVL_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + retval->funcs = &__PHYSFS_DirFunctions_MVL; + return(retval); + +MVL_openArchive_failed: + if (retval != NULL) + { + if (retval->opaque != NULL) + { + if (info->filename != NULL) + free(info->filename); + if (info->entries != NULL) + free(info->entries); + free(info); + } /* if */ + free(retval); + } /* if */ + + return(NULL); +} /* MVL_openArchive */ + + +static LinkedStringList *MVL_enumerateFiles(DirHandle *h, + const char *dirname, + int omitSymLinks) +{ + MVLinfo *info = ((MVLinfo *) h->opaque); + MVLentry *entry = info->entries; + LinkedStringList *retval = NULL, *p = NULL; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + /* no directories in MVL files. */ + BAIL_IF_MACRO(*dirname != '\0', ERR_NOT_A_DIR, NULL); + + for (i = 0; i < max; i++, entry++) + retval = __PHYSFS_addToLinkedStringList(retval, &p, entry->name, -1); + + return(retval); +} /* MVL_enumerateFiles */ + + +static MVLentry *mvl_find_entry(MVLinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + MVLentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_platformStricmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* mvl_find_entry */ + + +static int MVL_exists(DirHandle *h, const char *name) +{ + return(mvl_find_entry(((MVLinfo *) h->opaque), name) != NULL); +} /* MVL_exists */ + + +static int MVL_isDirectory(DirHandle *h, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(h, name); + return(0); /* never directories in a groupfile. */ +} /* MVL_isDirectory */ + + +static int MVL_isSymLink(DirHandle *h, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(h, name); + return(0); /* never symlinks in a groupfile. */ +} /* MVL_isSymLink */ + + +static PHYSFS_sint64 MVL_getLastModTime(DirHandle *h, + const char *name, + int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) h->opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (mvl_find_entry(info, name) != NULL); + if (*fileExists) /* use time of MVL itself in the physical filesystem. */ + retval = ((MVLinfo *) h->opaque)->last_mod_time; + + return(retval); +} /* MVL_getLastModTime */ + + +static FileHandle *MVL_openRead(DirHandle *h, const char *fnm, int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) h->opaque); + FileHandle *retval; + MVLfileinfo *finfo; + MVLentry *entry; + + entry = mvl_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + retval = (FileHandle *) malloc(sizeof (FileHandle)); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + finfo = (MVLfileinfo *) malloc(sizeof (MVLfileinfo)); + if (finfo == NULL) + { + free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + free(finfo); + free(retval); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + retval->opaque = (void *) finfo; + retval->funcs = &__PHYSFS_FileFunctions_MVL; + retval->dirHandle = h; + return(retval); +} /* MVL_openRead */ + + +static FileHandle *MVL_openWrite(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openWrite */ + + +static FileHandle *MVL_openAppend(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openAppend */ + + +static int MVL_remove(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_remove */ + + +static int MVL_mkdir(DirHandle *h, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_mkdir */ + +#endif /* defined PHYSFS_SUPPORTS_MVL */ + +/* end of mvl.c ... */ + diff --git a/configure.in b/configure.in index cfc7f5d1..bc6310da 100644 --- a/configure.in +++ b/configure.in @@ -128,6 +128,24 @@ if test x$enable_grp = xyes; then fi +dnl Check for hog archiver inclusion... +AC_ARG_ENABLE(hog, +[ --enable-hog enable Descent I/II HOG support [default=yes]], + , enable_hog=yes) +if test x$enable_hog = xyes; then + AC_DEFINE([PHYSFS_SUPPORTS_HOG], 1, [define if hog support is enabled]) +fi + + +dnl Check for mvl archiver inclusion... +AC_ARG_ENABLE(mvl, +[ --enable-mvl enable Descent II MVL support [default=yes]], + , enable_mvl=yes) +if test x$enable_mvl = xyes; then + AC_DEFINE([PHYSFS_SUPPORTS_MVL], 1, [define if mvl support is enabled]) +fi + + dnl Check for qpak archiver inclusion... AC_ARG_ENABLE(qpak, [ --enable-qpak enable Quake PAK support [default=yes]], diff --git a/physfs.c b/physfs.c index 516acb8e..80de2e12 100644 --- a/physfs.c +++ b/physfs.c @@ -59,6 +59,16 @@ extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP; extern const DirFunctions __PHYSFS_DirFunctions_GRP; #endif +#if (defined PHYSFS_SUPPORTS_HOG) +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG; +extern const DirFunctions __PHYSFS_DirFunctions_HOG; +#endif + +#if (defined PHYSFS_SUPPORTS_MVL) +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL; +extern const DirFunctions __PHYSFS_DirFunctions_MVL; +#endif + #if (defined PHYSFS_SUPPORTS_QPAK) extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK; extern const DirFunctions __PHYSFS_DirFunctions_QPAK; @@ -77,6 +87,14 @@ static const PHYSFS_ArchiveInfo *supported_types[] = &__PHYSFS_ArchiveInfo_GRP, #endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_ArchiveInfo_HOG, +#endif + +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_ArchiveInfo_MVL, +#endif + #if (defined PHYSFS_SUPPORTS_QPAK) &__PHYSFS_ArchiveInfo_QPAK, #endif @@ -94,6 +112,14 @@ static const DirFunctions *dirFunctions[] = &__PHYSFS_DirFunctions_GRP, #endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_DirFunctions_HOG, +#endif + +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_DirFunctions_MVL, +#endif + #if (defined PHYSFS_SUPPORTS_QPAK) &__PHYSFS_DirFunctions_QPAK, #endif diff --git a/physfs.h b/physfs.h index 17232f7c..8cd310b3 100644 --- a/physfs.h +++ b/physfs.h @@ -128,6 +128,8 @@ * Currently supported archive types: * - .ZIP (pkZip/WinZip/Info-ZIP compatible) * - .GRP (Build Engine groupfile archives) + * - .HOG (Descent I/II HOG file archives) + * - .MVL (Descent II movielib archives) * * Please see the file LICENSE in the source's root directory for licensing * and redistribution rights. diff --git a/physfs_internal.h b/physfs_internal.h index 430f5c1a..615cab60 100644 --- a/physfs_internal.h +++ b/physfs_internal.h @@ -46,6 +46,8 @@ extern "C" { #if (PHYSFS_LANG == PHYSFS_LANG_ENGLISH) #define DIR_ARCHIVE_DESCRIPTION "Non-archive, direct filesystem I/O" #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip compatible" #define ERR_IS_INITIALIZED "Already initialized" @@ -140,6 +142,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_GERMAN) #define DIR_ARCHIVE_DESCRIPTION "Kein Archiv, direkte Ein/Ausgabe in das Dateisystem" #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile Format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip kompatibel" #define ERR_IS_INITIALIZED "Bereits initialisiert" @@ -234,6 +238,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_KOI8_R) #define DIR_ARCHIVE_DESCRIPTION " , / " #define GRP_ARCHIVE_DESCRIPTION " Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip " #define ERR_IS_INITIALIZED " " @@ -328,6 +334,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_CP1251) #define DIR_ARCHIVE_DESCRIPTION " , / " #define GRP_ARCHIVE_DESCRIPTION " Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip " #define ERR_IS_INITIALIZED " " @@ -422,6 +430,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_CP866) #define DIR_ARCHIVE_DESCRIPTION " 娢, ।⢥ /뢮 䠩 ⥬" #define GRP_ARCHIVE_DESCRIPTION "ଠ 㯯 䠩 Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip ᮢ⨬" #define ERR_IS_INITIALIZED " 樠஢" @@ -516,6 +526,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_ISO_8859_5) #define DIR_ARCHIVE_DESCRIPTION " , / " #define GRP_ARCHIVE_DESCRIPTION " Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip " #define ERR_IS_INITIALIZED " " @@ -610,6 +622,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_SPANISH) #define DIR_ARCHIVE_DESCRIPTION "No es un archivo, E/S directa al sistema de ficheros" #define GRP_ARCHIVE_DESCRIPTION "Formato Build engine Groupfile" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "Compatible con PkZip/WinZip/Info-Zip" #define ERR_IS_INITIALIZED "Ya estaba inicializado" @@ -704,6 +718,8 @@ extern "C" { #elif (PHYSFS_LANG == PHYSFS_LANG_FRENCH) #define DIR_ARCHIVE_DESCRIPTION "Pas d'archive, E/S directes sur systme de fichiers" #define GRP_ARCHIVE_DESCRIPTION "Format Groupfile du moteur Build" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" #define ZIP_ARCHIVE_DESCRIPTION "Compatible PkZip/WinZip/Info-Zip" #define ERR_IS_INITIALIZED "Dj initialis"