From fabb803a655904ba6d5f2aba50265f07507e31e9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 9 Jul 2001 00:49:41 +0000 Subject: [PATCH] Initial add. Implemented, buggy, but not crashing. --- archivers/grp.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 archivers/grp.c diff --git a/archivers/grp.c b/archivers/grp.c new file mode 100644 index 00000000..a38fb4de --- /dev/null +++ b/archivers/grp.c @@ -0,0 +1,375 @@ +/* + * GRP support routines for PhysicsFS. + * + * This driver handles BUILD engine archives ("groupfiles"). This format + * (but not this driver) was put together by Ken Silverman. + * + * The format is simple enough. In Ken's words: + * + * What's the .GRP file format? + * + * The ".grp" file format is just a collection of a lot of files stored + * into 1 big one. I tried to make the format as simple as possible: The + * first 12 bytes contains my name, "KenSilverman". The next 4 bytes is + * the number of files that were compacted into the group file. Then for + * each file, there is a 16 byte structure, where the first 12 bytes are + * the filename, and the last 4 bytes are the file's size. The rest of + * the group file is just the raw data packed one after the other in the + * same order as the list of files. + * + * (That info is from http://www.advsys.net/ken/build.htm ...) + * + * As it was never a concern in the DOS-based Duke Nukem days, I treat these + * archives as CASE-INSENSITIVE. Opening "myfile.txt" and "MYFILE.TXT" both + * work, and get the same file. + * + * Please see the file LICENSE in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if (!defined PHYSFS_SUPPORTS_GRP) +#error PHYSFS_SUPPORTS_GRP must be defined. +#endif + + +typedef struct +{ + FILE *handle; + int totalEntries; +} GRPinfo; + +typedef struct +{ + int startPos; + int curPos; + int size; +} GRPfileinfo; + + +extern const DirFunctions __PHYSFS_DirFunctions_GRP; +static const FileFunctions __PHYSFS_FileFunctions_GRP; + + +static int GRP_read(FileHandle *handle, void *buffer, + unsigned int objSize, unsigned int objCount) +{ + GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque); + FILE *fh = (FILE *) (((GRPinfo *) (handle->dirHandle->opaque))->handle); + int bytesLeft = (finfo->startPos + finfo->size) - finfo->curPos; + int objsLeft = bytesLeft / objSize; + int retval = 0; + + if (objsLeft < objCount) + objCount = objsLeft; + + errno = 0; + BAIL_IF_MACRO(fseek(fh,finfo->curPos,SEEK_SET) == -1,strerror(errno),-1); + + errno = 0; + retval = fread(buffer, objSize, objCount, fh); + finfo->curPos += retval; + BAIL_IF_MACRO((retval < objCount) && (ferror(fh)),strerror(errno),retval); + + return(retval); +} /* GRP_read */ + + +static int GRP_eof(FileHandle *handle) +{ + GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque); + return(finfo->curPos >= finfo->startPos + finfo->size); +} /* GRP_eof */ + + +static int GRP_tell(FileHandle *handle) +{ + GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque); + return(finfo->curPos - finfo->startPos); +} /* GRP_tell */ + + +static int GRP_seek(FileHandle *handle, int offset) +{ + GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque); + int newPos = finfo->curPos + offset; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(newPos > finfo->startPos + finfo->size, ERR_PAST_EOF, 0); + finfo->curPos = newPos; + return(1); +} /* GRP_seek */ + + +static int GRP_fileClose(FileHandle *handle) +{ + free(handle->opaque); + free(handle); + return(1); +} /* GRP_fileClose */ + + +static int openGrp(const char *filename, int forWriting, FILE **fh, int *count) +{ + char buf[12]; + + assert(sizeof (int) == 4); + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + errno = 0; + *fh = fopen(filename, "rb"); + BAIL_IF_MACRO(*fh == NULL, strerror(errno), 0); + + errno = 0; + BAIL_IF_MACRO(fread(buf, 12, 1, *fh) != 1, strerror(errno), 0); + BAIL_IF_MACRO(strncmp(buf, "KenSilverman", 12) != 0, + ERR_UNSUPPORTED_ARCHIVE, 0); + + if (fread(count, 4, 1, *fh) != 1) + *count = 0; + + return(1); +} /* openGrp */ + + +static int GRP_isArchive(const char *filename, int forWriting) +{ + FILE *fh; + int fileCount; + int retval = openGrp(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + fclose(fh); + + return(retval); +} /* GRP_isArchive */ + + +static DirHandle *GRP_openArchive(const char *name, int forWriting) +{ + FILE *fh; + int fileCount; + DirHandle *retval = malloc(sizeof (DirHandle)); + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + retval->opaque = malloc(sizeof (GRPinfo)); + if (retval->opaque == NULL) + { + free(retval); + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + if (!openGrp(name, forWriting, &fh, &fileCount)) + { + if (fh != NULL) + fclose(fh); + free(retval->opaque); + free(retval); + } /* if */ + + ((GRPinfo *) retval->opaque)->handle = fh; + ((GRPinfo *) retval->opaque)->totalEntries = fileCount; + retval->funcs = &__PHYSFS_DirFunctions_GRP; + return(retval); +} /* GRP_openArchive */ + + +static LinkedStringList *GRP_enumerateFiles(DirHandle *h, const char *dirname) +{ + char buf[16]; + GRPinfo *g = (GRPinfo *) (h->opaque); + FILE *fh = g->handle; + int i; + LinkedStringList *retval = NULL; + LinkedStringList *l = NULL; + LinkedStringList *prev = NULL; + + /* jump to first file entry... */ + errno = 0; + BAIL_IF_MACRO(fseek(fh, 16, SEEK_SET) == -1, strerror(errno), NULL); + + for (i = 0; i < g->totalEntries; i++) + { + errno = 0; + BAIL_IF_MACRO(fread(buf, 16, 1, fh) != 1, strerror(errno), retval); + + buf[12] = '\0'; /* FILENAME.EXT is all you get. */ + + l = (LinkedStringList *) malloc(sizeof (LinkedStringList)); + if (l != NULL) + break; + + l->str = (char *) malloc(strlen(buf) + 1); + if (l->str == NULL) + { + free(l); + break; + } /* if */ + + if (retval == NULL) + retval = l; + else + prev->next = l; + + prev = l; + l->next = NULL; + } /* for */ + + return(retval); +} /* GRP_enumerateFiles */ + + +static int getFileEntry(DirHandle *h, const char *name, int *size) +{ + char buf[16]; + GRPinfo *g = (GRPinfo *) (h->opaque); + FILE *fh = g->handle; + int i; + char *ptr; + int retval = 0; /*(g->totalEntries + 1) * 16;*/ /* offset of raw file data */ + + /* Rule out filenames to avoid unneeded file i/o... */ + if (strchr(name, '/') != NULL) /* no directories in groupfiles. */ + return(-1); + + ptr = strchr(name, '.'); + if ((ptr) && (strlen(ptr) > 4)) /* 3 char extension at most. */ + return(-1); + + if (strlen(name) > 12) + return(-1); + + /* jump to first file entry... */ + errno = 0; + BAIL_IF_MACRO(fseek(fh, 16, SEEK_SET) == -1, strerror(errno), -1); + + for (i = 0; i < g->totalEntries; i++) + { + int fsize; + + errno = 0; + BAIL_IF_MACRO(fread(buf, 16, 1, fh) != 1, strerror(errno), -1); + + fsize = *((int *) (buf + 12)); + + buf[12] = '\0'; /* FILENAME.EXT is all you get. */ + + if (__PHYSFS_platformStricmp(buf, name) == 0) + { + if (size != NULL) + *size = fsize; + return(retval); + } /* if */ + + retval += fsize; + } /* for */ + + return(-1); /* not found. */ +} /* getFileEntry */ + + +static int GRP_exists(DirHandle *h, const char *name) +{ + return(getFileEntry(h, name, NULL) != -1); +} /* GRP_exists */ + + +static int GRP_isDirectory(DirHandle *h, const char *name) +{ + return(0); /* never directories in a groupfile. */ +} /* GRP_isDirectory */ + + +static int GRP_isSymLink(DirHandle *h, const char *name) +{ + return(0); /* never symlinks in a groupfile. */ +} /* GRP_isSymLink */ + + +static FileHandle *GRP_openRead(DirHandle *h, const char *name) +{ + FileHandle *retval; + GRPfileinfo *finfo; + int size, offset; + + offset = getFileEntry(h, name, &size); + BAIL_IF_MACRO(offset == -1, ERR_NO_SUCH_FILE, NULL); + + retval = (FileHandle *) malloc(sizeof (FileHandle)); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + finfo = (GRPfileinfo *) malloc(sizeof (GRPfileinfo)); + if (finfo == NULL) + { + free(retval); + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + finfo->startPos = offset; + finfo->curPos = offset; + finfo->size = size; + retval->opaque = (void *) finfo; + retval->funcs = &__PHYSFS_FileFunctions_GRP; + retval->dirHandle = h; + return(retval); +} /* GRP_openRead */ + + +static void GRP_dirClose(DirHandle *h) +{ + fclose( ((GRPinfo *) h->opaque)->handle ); + free(h->opaque); + free(h); +} /* GRP_dirClose */ + + +static const FileFunctions __PHYSFS_FileFunctions_GRP = +{ + GRP_read, /* read() method */ + NULL, /* write() method */ + GRP_eof, /* eof() method */ + GRP_tell, /* tell() method */ + GRP_seek, /* seek() method */ + GRP_fileClose /* fileClose() method */ +}; + + +const DirFunctions __PHYSFS_DirFunctions_GRP = +{ + GRP_isArchive, /* isArchive() method */ + GRP_openArchive, /* openArchive() method */ + GRP_enumerateFiles, /* enumerateFiles() method */ + GRP_exists, /* exists() method */ + GRP_isDirectory, /* isDirectory() method */ + GRP_isSymLink, /* isSymLink() method */ + GRP_openRead, /* openRead() method */ + NULL, /* openWrite() method */ + NULL, /* openAppend() method */ + NULL, /* remove() method */ + NULL, /* mkdir() method */ + GRP_dirClose /* dirClose() method */ +}; + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP = +{ + "GRP", + "Build engine Groupfile format" +}; + +/* end of grp.c ... */ +