Add PhysFS 2.0.3.

This commit is contained in:
rude
2013-09-25 13:01:20 +02:00
parent f2a8dd670a
commit b170f825e8
523 changed files with 98507 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
/*
* Standard directory I/O support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
PHYSFS_sint64 retval;
retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount);
return(retval);
} /* DIR_read */
static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
PHYSFS_sint64 retval;
retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount);
return(retval);
} /* DIR_write */
static int DIR_eof(fvoid *opaque)
{
return(__PHYSFS_platformEOF(opaque));
} /* DIR_eof */
static PHYSFS_sint64 DIR_tell(fvoid *opaque)
{
return(__PHYSFS_platformTell(opaque));
} /* DIR_tell */
static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
return(__PHYSFS_platformSeek(opaque, offset));
} /* DIR_seek */
static PHYSFS_sint64 DIR_fileLength(fvoid *opaque)
{
return(__PHYSFS_platformFileLength(opaque));
} /* DIR_fileLength */
static int DIR_fileClose(fvoid *opaque)
{
/*
* we manually flush the buffer, since that's the place a close will
* most likely fail, but that will leave the file handle in an undefined
* state if it fails. Flush failures we can recover from.
*/
BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0);
BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0);
return(1);
} /* DIR_fileClose */
static int DIR_isArchive(const char *filename, int forWriting)
{
/* directories ARE archives in this driver... */
return(__PHYSFS_platformIsDirectory(filename));
} /* DIR_isArchive */
static void *DIR_openArchive(const char *name, int forWriting)
{
const char *dirsep = PHYSFS_getDirSeparator();
char *retval = NULL;
size_t namelen = strlen(name);
size_t seplen = strlen(dirsep);
/* !!! FIXME: when is this not called right before openArchive? */
BAIL_IF_MACRO(!DIR_isArchive(name, forWriting),
ERR_UNSUPPORTED_ARCHIVE, 0);
retval = allocator.Malloc(namelen + seplen + 1);
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
/* make sure there's a dir separator at the end of the string */
strcpy(retval, name);
if (strcmp((name + namelen) - seplen, dirsep) != 0)
strcat(retval, dirsep);
return(retval);
} /* DIR_openArchive */
static void DIR_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL);
if (d != NULL)
{
__PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb,
origdir, callbackdata);
allocator.Free(d);
} /* if */
} /* DIR_enumerateFiles */
static int DIR_exists(dvoid *opaque, const char *name)
{
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
int retval;
BAIL_IF_MACRO(f == NULL, NULL, 0);
retval = __PHYSFS_platformExists(f);
allocator.Free(f);
return(retval);
} /* DIR_exists */
static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
int retval = 0;
BAIL_IF_MACRO(d == NULL, NULL, 0);
*fileExists = __PHYSFS_platformExists(d);
if (*fileExists)
retval = __PHYSFS_platformIsDirectory(d);
allocator.Free(d);
return(retval);
} /* DIR_isDirectory */
static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
int retval = 0;
BAIL_IF_MACRO(f == NULL, NULL, 0);
*fileExists = __PHYSFS_platformExists(f);
if (*fileExists)
retval = __PHYSFS_platformIsSymLink(f);
allocator.Free(f);
return(retval);
} /* DIR_isSymLink */
static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
PHYSFS_sint64 retval = -1;
BAIL_IF_MACRO(d == NULL, NULL, 0);
*fileExists = __PHYSFS_platformExists(d);
if (*fileExists)
retval = __PHYSFS_platformGetLastModTime(d);
allocator.Free(d);
return(retval);
} /* DIR_getLastModTime */
static fvoid *doOpen(dvoid *opaque, const char *name,
void *(*openFunc)(const char *filename),
int *fileExists)
{
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
void *rc = NULL;
BAIL_IF_MACRO(f == NULL, NULL, NULL);
if (fileExists != NULL)
{
*fileExists = __PHYSFS_platformExists(f);
if (!(*fileExists))
{
allocator.Free(f);
return(NULL);
} /* if */
} /* if */
rc = openFunc(f);
allocator.Free(f);
return((fvoid *) rc);
} /* doOpen */
static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist)
{
return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist));
} /* DIR_openRead */
static fvoid *DIR_openWrite(dvoid *opaque, const char *filename)
{
return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL));
} /* DIR_openWrite */
static fvoid *DIR_openAppend(dvoid *opaque, const char *filename)
{
return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL));
} /* DIR_openAppend */
static int DIR_remove(dvoid *opaque, const char *name)
{
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
int retval;
BAIL_IF_MACRO(f == NULL, NULL, 0);
retval = __PHYSFS_platformDelete(f);
allocator.Free(f);
return(retval);
} /* DIR_remove */
static int DIR_mkdir(dvoid *opaque, const char *name)
{
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
int retval;
BAIL_IF_MACRO(f == NULL, NULL, 0);
retval = __PHYSFS_platformMkDir(f);
allocator.Free(f);
return(retval);
} /* DIR_mkdir */
static void DIR_dirClose(dvoid *opaque)
{
allocator.Free(opaque);
} /* DIR_dirClose */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR =
{
"",
DIR_ARCHIVE_DESCRIPTION,
"Ryan C. Gordon <icculus@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
{
&__PHYSFS_ArchiveInfo_DIR,
DIR_isArchive, /* isArchive() method */
DIR_openArchive, /* openArchive() method */
DIR_enumerateFiles, /* enumerateFiles() method */
DIR_exists, /* exists() method */
DIR_isDirectory, /* isDirectory() method */
DIR_isSymLink, /* isSymLink() method */
DIR_getLastModTime, /* getLastModTime() method */
DIR_openRead, /* openRead() method */
DIR_openWrite, /* openWrite() method */
DIR_openAppend, /* openAppend() method */
DIR_remove, /* remove() method */
DIR_mkdir, /* mkdir() method */
DIR_dirClose, /* dirClose() method */
DIR_read, /* read() method */
DIR_write, /* write() method */
DIR_eof, /* eof() method */
DIR_tell, /* tell() method */
DIR_seek, /* seek() method */
DIR_fileLength, /* fileLength() method */
DIR_fileClose /* fileClose() method */
};
/* end of dir.c ... */
+475
View File
@@ -0,0 +1,475 @@
/*
* 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 ...)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_GRP)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
char name[13];
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
} GRPentry;
typedef struct
{
char *filename;
PHYSFS_sint64 last_mod_time;
PHYSFS_uint32 entryCount;
GRPentry *entries;
} GRPinfo;
typedef struct
{
void *handle;
GRPentry *entry;
PHYSFS_uint32 curPos;
} GRPfileinfo;
static void GRP_dirClose(dvoid *opaque)
{
GRPinfo *info = ((GRPinfo *) opaque);
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
} /* GRP_dirClose */
static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
GRPentry *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);
} /* GRP_read */
static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* GRP_write */
static int GRP_eof(fvoid *opaque)
{
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
GRPentry *entry = finfo->entry;
return(finfo->curPos >= entry->size);
} /* GRP_eof */
static PHYSFS_sint64 GRP_tell(fvoid *opaque)
{
return(((GRPfileinfo *) opaque)->curPos);
} /* GRP_tell */
static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
GRPentry *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);
} /* GRP_seek */
static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
{
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
return((PHYSFS_sint64) finfo->entry->size);
} /* GRP_fileLength */
static int GRP_fileClose(fvoid *opaque)
{
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
allocator.Free(finfo);
return(1);
} /* GRP_fileClose */
static int grp_open(const char *filename, int forWriting,
void **fh, PHYSFS_uint32 *count)
{
PHYSFS_uint8 buf[12];
*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, 12, 1) != 1)
goto openGrp_failed;
if (memcmp(buf, "KenSilverman", 12) != 0)
{
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
goto openGrp_failed;
} /* if */
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
goto openGrp_failed;
*count = PHYSFS_swapULE32(*count);
return(1);
openGrp_failed:
if (*fh != NULL)
__PHYSFS_platformClose(*fh);
*count = -1;
*fh = NULL;
return(0);
} /* grp_open */
static int GRP_isArchive(const char *filename, int forWriting)
{
void *fh;
PHYSFS_uint32 fileCount;
int retval = grp_open(filename, forWriting, &fh, &fileCount);
if (fh != NULL)
__PHYSFS_platformClose(fh);
return(retval);
} /* GRP_isArchive */
static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
const GRPentry *a = (const GRPentry *) _a;
return(strcmp(a[one].name, a[two].name));
} /* if */
return 0;
} /* grp_entry_cmp */
static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
GRPentry tmp;
GRPentry *first = &(((GRPentry *) _a)[one]);
GRPentry *second = &(((GRPentry *) _a)[two]);
memcpy(&tmp, first, sizeof (GRPentry));
memcpy(first, second, sizeof (GRPentry));
memcpy(second, &tmp, sizeof (GRPentry));
} /* if */
} /* grp_entry_swap */
static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
{
void *fh = NULL;
PHYSFS_uint32 fileCount;
PHYSFS_uint32 location = 16; /* sizeof sig. */
GRPentry *entry;
char *ptr;
BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
info->entryCount = fileCount;
info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
if (info->entries == NULL)
{
__PHYSFS_platformClose(fh);
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
} /* if */
location += (16 * fileCount);
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
{
if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
entry->name[12] = '\0'; /* name isn't null-terminated in file. */
if ((ptr = strchr(entry->name, ' ')) != NULL)
*ptr = '\0'; /* trim extra spaces. */
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,
grp_entry_cmp, grp_entry_swap);
return(1);
} /* grp_load_entries */
static void *GRP_openArchive(const char *name, int forWriting)
{
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
memset(info, '\0', sizeof (GRPinfo));
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
if (!grp_load_entries(name, forWriting, info))
goto GRP_openArchive_failed;
strcpy(info->filename, name);
info->last_mod_time = modtime;
return(info);
GRP_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
allocator.Free(info->filename);
if (info->entries != NULL)
allocator.Free(info->entries);
allocator.Free(info);
} /* if */
return(NULL);
} /* GRP_openArchive */
static void GRP_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
/* no directories in GRP files. */
if (*dname == '\0')
{
GRPinfo *info = (GRPinfo *) opaque;
GRPentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
for (i = 0; i < max; i++, entry++)
cb(callbackdata, origdir, entry->name);
} /* if */
} /* GRP_enumerateFiles */
static GRPentry *grp_find_entry(GRPinfo *info, const char *name)
{
char *ptr = strchr(name, '.');
GRPentry *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 = strcmp(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);
} /* grp_find_entry */
static int GRP_exists(dvoid *opaque, const char *name)
{
return(grp_find_entry((GRPinfo *) opaque, name) != NULL);
} /* GRP_exists */
static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = GRP_exists(opaque, name);
return(0); /* never directories in a groupfile. */
} /* GRP_isDirectory */
static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = GRP_exists(opaque, name);
return(0); /* never symlinks in a groupfile. */
} /* GRP_isSymLink */
static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
GRPinfo *info = (GRPinfo *) opaque;
PHYSFS_sint64 retval = -1;
*fileExists = (grp_find_entry(info, name) != NULL);
if (*fileExists) /* use time of GRP itself in the physical filesystem. */
retval = info->last_mod_time;
return(retval);
} /* GRP_getLastModTime */
static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
{
GRPinfo *info = (GRPinfo *) opaque;
GRPfileinfo *finfo;
GRPentry *entry;
entry = grp_find_entry(info, fnm);
*fileExists = (entry != NULL);
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if ( (finfo->handle == NULL) ||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
{
allocator.Free(finfo);
return(NULL);
} /* if */
finfo->curPos = 0;
finfo->entry = entry;
return(finfo);
} /* GRP_openRead */
static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* GRP_openWrite */
static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* GRP_openAppend */
static int GRP_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* GRP_remove */
static int GRP_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* GRP_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
{
"GRP",
GRP_ARCHIVE_DESCRIPTION,
"Ryan C. Gordon <icculus@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
{
&__PHYSFS_ArchiveInfo_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_getLastModTime, /* getLastModTime() method */
GRP_openRead, /* openRead() method */
GRP_openWrite, /* openWrite() method */
GRP_openAppend, /* openAppend() method */
GRP_remove, /* remove() method */
GRP_mkdir, /* mkdir() method */
GRP_dirClose, /* dirClose() method */
GRP_read, /* read() method */
GRP_write, /* write() method */
GRP_eof, /* eof() method */
GRP_tell, /* tell() method */
GRP_seek, /* seek() method */
GRP_fileLength, /* fileLength() method */
GRP_fileClose /* fileClose() method */
};
#endif /* defined PHYSFS_SUPPORTS_GRP */
/* end of grp.c ... */
+514
View File
@@ -0,0 +1,514 @@
/*
* 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.txt in the source's root directory.
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_HOG)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(dvoid *opaque)
{
HOGinfo *info = ((HOGinfo *) opaque);
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
} /* HOG_dirClose */
static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
HOGfileinfo *finfo = (HOGfileinfo *) 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(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* HOG_write */
static int HOG_eof(fvoid *opaque)
{
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
HOGentry *entry = finfo->entry;
return(finfo->curPos >= entry->size);
} /* HOG_eof */
static PHYSFS_sint64 HOG_tell(fvoid *opaque)
{
return(((HOGfileinfo *) opaque)->curPos);
} /* HOG_tell */
static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
HOGfileinfo *finfo = (HOGfileinfo *) 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(fvoid *opaque)
{
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
return((PHYSFS_sint64) finfo->entry->size);
} /* HOG_fileLength */
static int HOG_fileClose(fvoid *opaque)
{
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
allocator.Free(finfo);
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;
} /* while */
/* 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)
{
if (one != two)
{
const HOGentry *a = (const HOGentry *) _a;
return(__PHYSFS_stricmpASCII(a[one].name, a[two].name));
} /* if */
return 0;
} /* hog_entry_cmp */
static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != 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));
} /* if */
} /* hog_entry_swap */
static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
{
void *fh = NULL;
PHYSFS_uint32 fileCount;
HOGentry *entry;
BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0);
info->entryCount = fileCount;
info->entries = (HOGentry *) allocator.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 = (unsigned int) __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 void *HOG_openArchive(const char *name, int forWriting)
{
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
memset(info, '\0', sizeof (HOGinfo));
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
if (!hog_load_entries(name, forWriting, info))
goto HOG_openArchive_failed;
strcpy(info->filename, name);
info->last_mod_time = modtime;
return(info);
HOG_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
allocator.Free(info->filename);
if (info->entries != NULL)
allocator.Free(info->entries);
allocator.Free(info);
} /* if */
return(NULL);
} /* HOG_openArchive */
static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
/* no directories in HOG files. */
if (*dname == '\0')
{
HOGinfo *info = (HOGinfo *) opaque;
HOGentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
for (i = 0; i < max; i++, entry++)
cb(callbackdata, origdir, entry->name);
} /* if */
} /* 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_stricmpASCII(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(dvoid *opaque, const char *name)
{
return(hog_find_entry(((HOGinfo *) opaque), name) != NULL);
} /* HOG_exists */
static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = HOG_exists(opaque, name);
return(0); /* never directories in a groupfile. */
} /* HOG_isDirectory */
static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = HOG_exists(opaque, name);
return(0); /* never symlinks in a groupfile. */
} /* HOG_isSymLink */
static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
HOGinfo *info = ((HOGinfo *) opaque);
PHYSFS_sint64 retval = -1;
*fileExists = (hog_find_entry(info, name) != NULL);
if (*fileExists) /* use time of HOG itself in the physical filesystem. */
retval = info->last_mod_time;
return(retval);
} /* HOG_getLastModTime */
static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
{
HOGinfo *info = ((HOGinfo *) opaque);
HOGfileinfo *finfo;
HOGentry *entry;
entry = hog_find_entry(info, fnm);
*fileExists = (entry != NULL);
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if ( (finfo->handle == NULL) ||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
{
allocator.Free(finfo);
return(NULL);
} /* if */
finfo->curPos = 0;
finfo->entry = entry;
return(finfo);
} /* HOG_openRead */
static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* HOG_openWrite */
static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* HOG_openAppend */
static int HOG_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* HOG_remove */
static int HOG_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* HOG_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
{
"HOG",
HOG_ARCHIVE_DESCRIPTION,
"Bradley Bell <btb@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_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 */
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 */
};
#endif /* defined PHYSFS_SUPPORTS_HOG */
/* end of hog.c ... */
+736
View File
@@ -0,0 +1,736 @@
/*
* LZMA support routines for PhysicsFS.
*
* Please see the file lzma.txt in the lzma/ directory.
*
* This file was written by Dennis Schridde, with some peeking at "7zMain.c"
* by Igor Pavlov.
*/
#if (defined PHYSFS_SUPPORTS_7Z)
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#include "lzma/C/7zCrc.h"
#include "lzma/C/Archive/7z/7zIn.h"
#include "lzma/C/Archive/7z/7zExtract.h"
/* 7z internal from 7zIn.c */
extern int TestSignatureCandidate(Byte *testBytes);
#ifdef _LZMA_IN_CB
# define BUFFER_SIZE (1 << 12)
#endif /* _LZMA_IN_CB */
/*
* Carries filestream metadata through 7z
*/
typedef struct _FileInputStream
{
ISzAlloc allocImp; /* Allocation implementation, used by 7z */
ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
void *file; /* Filehandle, used by read implementation */
#ifdef _LZMA_IN_CB
Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
#endif /* _LZMA_IN_CB */
} FileInputStream;
/*
* In the 7z format archives are splited into blocks, those are called folders
* Set by LZMA_read()
*/
typedef struct _LZMAfolder
{
PHYSFS_uint32 index; /* Index of folder in archive */
PHYSFS_uint32 references; /* Number of files using this block */
PHYSFS_uint8 *cache; /* Cached folder */
size_t size; /* Size of folder */
} LZMAfolder;
/*
* Set by LZMA_openArchive(), except folder which gets it's values
* in LZMA_read()
*/
typedef struct _LZMAarchive
{
struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */
LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */
CArchiveDatabaseEx db; /* For 7z: Database */
FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */
} LZMAarchive;
/* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */
typedef struct _LZMAfile
{
PHYSFS_uint32 index; /* Index of file in archive */
LZMAarchive *archive; /* Link to corresponding archive */
LZMAfolder *folder; /* Link to corresponding folder */
CFileItem *item; /* For 7z: File info, eg. name, size */
size_t offset; /* Offset in folder */
size_t position; /* Current "virtual" position in file */
} LZMAfile;
/* Memory management implementations to be passed to 7z */
static void *SzAllocPhysicsFS(size_t size)
{
return ((size == 0) ? NULL : allocator.Malloc(size));
} /* SzAllocPhysicsFS */
static void SzFreePhysicsFS(void *address)
{
if (address != NULL)
allocator.Free(address);
} /* SzFreePhysicsFS */
/* Filesystem implementations to be passed to 7z */
#ifdef _LZMA_IN_CB
/*
* Read implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
size_t *processedSize)
{
FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */
PHYSFS_sint64 processedSizeLoc = 0;
if (maxReqSize > BUFFER_SIZE)
maxReqSize = BUFFER_SIZE;
processedSizeLoc = __PHYSFS_platformRead(s->file, s->buffer, 1, maxReqSize);
*buffer = s->buffer;
if (processedSize != NULL)
*processedSize = (size_t) processedSizeLoc;
return SZ_OK;
} /* SzFileReadImp */
#else
/*
* Read implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
size_t *processedSize)
{
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
size_t processedSizeLoc = __PHYSFS_platformRead(s->file, buffer, 1, size);
if (processedSize != 0)
*processedSize = processedSizeLoc;
return SZ_OK;
} /* SzFileReadImp */
#endif
/*
* Seek implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
{
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
if (__PHYSFS_platformSeek(s->file, (PHYSFS_uint64) pos))
return SZ_OK;
return SZE_FAIL;
} /* SzFileSeekImp */
/*
* Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp
*/
static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft)
{
/* MS counts in nanoseconds ... */
const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000);
/* MS likes to count seconds since 01.01.1601 ... */
const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600);
PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32);
return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF;
} /* lzma_filetime_to_unix_timestamp */
/*
* Compare a file with a given name, C89 stdlib variant
* Used for sorting
*/
static int lzma_file_cmp_stdlib(const void *key, const void *object)
{
const char *name = (const char *) key;
LZMAfile *file = (LZMAfile *) object;
return(strcmp(name, file->item->Name));
} /* lzma_file_cmp_posix */
/*
* Compare two files with each other based on the name
* Used for sorting
*/
static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
LZMAfile *files = (LZMAfile *) _a;
return(strcmp(files[one].item->Name, files[two].item->Name));
} /* lzma_file_cmp */
/*
* Swap two entries in the file array
*/
static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
LZMAfile tmp;
LZMAfile *first = &(((LZMAfile *) _a)[one]);
LZMAfile *second = &(((LZMAfile *) _a)[two]);
memcpy(&tmp, first, sizeof (LZMAfile));
memcpy(first, second, sizeof (LZMAfile));
memcpy(second, &tmp, sizeof (LZMAfile));
} /* lzma_file_swap */
/*
* Find entry 'name' in 'archive'
*/
static LZMAfile * lzma_find_file(LZMAarchive *archive, const char *name)
{
LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
return(file);
} /* lzma_find_file */
/*
* Load metadata for the file at given index
*/
static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex)
{
LZMAfile *file = &archive->files[fileIndex];
PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
file->index = fileIndex; /* Store index into 7z array, since we sort our own. */
file->archive = archive;
file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */
file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */
file->position = 0;
file->offset = 0; /* Offset will be set by LZMA_read() */
return(1);
} /* lzma_load_file */
/*
* Load metadata for all files
*/
static int lzma_files_init(LZMAarchive *archive)
{
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
for (fileIndex = 0; fileIndex < numFiles; fileIndex++ )
{
if (!lzma_file_init(archive, fileIndex))
{
return(0); /* FALSE on failure */
}
} /* for */
__PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap);
return(1);
} /* lzma_load_files */
/*
* Initialise specified archive
*/
static void lzma_archive_init(LZMAarchive *archive)
{
memset(archive, 0, sizeof(*archive));
/* Prepare callbacks for 7z */
archive->stream.inStream.Read = SzFileReadImp;
archive->stream.inStream.Seek = SzFileSeekImp;
archive->stream.allocImp.Alloc = SzAllocPhysicsFS;
archive->stream.allocImp.Free = SzFreePhysicsFS;
archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS;
archive->stream.allocTempImp.Free = SzFreePhysicsFS;
}
/*
* Deinitialise archive
*/
static void lzma_archive_exit(LZMAarchive *archive)
{
/* Free arrays */
allocator.Free(archive->folders);
allocator.Free(archive->files);
allocator.Free(archive);
}
/*
* Wrap all 7z calls in this, so the physfs error state is set appropriately.
*/
static int lzma_err(SZ_RESULT rc)
{
switch (rc)
{
case SZ_OK: /* Same as LZMA_RESULT_OK */
break;
case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
__PHYSFS_setError(ERR_DATA_ERROR);
break;
case SZE_OUTOFMEMORY:
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
break;
case SZE_CRC_ERROR:
__PHYSFS_setError(ERR_CORRUPTED);
break;
case SZE_NOTIMPL:
__PHYSFS_setError(ERR_NOT_IMPLEMENTED);
break;
case SZE_FAIL:
__PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */
break;
case SZE_ARCHIVE_ERROR:
__PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */
break;
default:
__PHYSFS_setError(ERR_UNKNOWN_ERROR);
} /* switch */
return(rc);
} /* lzma_err */
static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
LZMAfile *file = (LZMAfile *) opaque;
size_t wantedSize = objSize*objCount;
size_t remainingSize = file->item->Size - file->position;
size_t fileSize = 0;
BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */
BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0);
if (remainingSize < wantedSize)
{
wantedSize = remainingSize - (remainingSize % objSize);
objCount = (PHYSFS_uint32) (remainingSize / objSize);
BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */
__PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */
} /* if */
/* Only decompress the folder if it is not allready cached */
if (file->folder->cache == NULL)
{
int rc = lzma_err(SzExtract(
&file->archive->stream.inStream, /* compressed data */
&file->archive->db, /* 7z's database, containing everything */
file->index, /* Index into database arrays */
/* Index of cached folder, will be changed by SzExtract */
&file->folder->index,
/* Cache for decompressed folder, allocated/freed by SzExtract */
&file->folder->cache,
/* Size of cache, will be changed by SzExtract */
&file->folder->size,
/* Offset of this file inside the cache, set by SzExtract */
&file->offset,
&fileSize, /* Size of this file */
&file->archive->stream.allocImp,
&file->archive->stream.allocTempImp));
if (rc != SZ_OK)
return -1;
} /* if */
/* Copy wanted bytes over from cache to outBuffer */
memcpy(outBuffer,
(file->folder->cache +
file->offset + file->position),
wantedSize);
file->position += wantedSize; /* Increase virtual position */
return objCount;
} /* LZMA_read */
static PHYSFS_sint64 LZMA_write(fvoid *opaque, const void *buf,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* LZMA_write */
static int LZMA_eof(fvoid *opaque)
{
LZMAfile *file = (LZMAfile *) opaque;
return (file->position >= file->item->Size);
} /* LZMA_eof */
static PHYSFS_sint64 LZMA_tell(fvoid *opaque)
{
LZMAfile *file = (LZMAfile *) opaque;
return (file->position);
} /* LZMA_tell */
static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
LZMAfile *file = (LZMAfile *) opaque;
BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0);
BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0);
file->position = offset; /* We only use a virtual position... */
return 1;
} /* LZMA_seek */
static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque)
{
LZMAfile *file = (LZMAfile *) opaque;
return (file->item->Size);
} /* LZMA_fileLength */
static int LZMA_fileClose(fvoid *opaque)
{
LZMAfile *file = (LZMAfile *) opaque;
BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, 0);
/* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
if (file->folder->references > 0)
file->folder->references--;
if (file->folder->references == 0)
{
/* Free the cache which might have been allocated by LZMA_read() */
allocator.Free(file->folder->cache);
file->folder->cache = NULL;
}
return(1);
} /* LZMA_fileClose */
static int LZMA_isArchive(const char *filename, int forWriting)
{
PHYSFS_uint8 sig[k7zSignatureSize];
void *in;
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
in = __PHYSFS_platformOpenRead(filename);
BAIL_IF_MACRO(in == NULL, NULL, 0);
/* Read signature bytes */
if (__PHYSFS_platformRead(in, sig, k7zSignatureSize, 1) != 1)
{
__PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */
BAIL_MACRO(NULL, 0);
}
__PHYSFS_platformClose(in);
/* Test whether sig is the 7z signature */
return(TestSignatureCandidate(sig));
} /* LZMA_isArchive */
static void *LZMA_openArchive(const char *name, int forWriting)
{
size_t len = 0;
LZMAarchive *archive = NULL;
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
BAIL_IF_MACRO(!LZMA_isArchive(name,forWriting), ERR_UNSUPPORTED_ARCHIVE, 0);
archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL);
lzma_archive_init(archive);
if ( (archive->stream.file = __PHYSFS_platformOpenRead(name)) == NULL )
{
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
return(NULL); /* Error is set by platformOpenRead! */
}
CrcGenerateTable();
SzArDbExInit(&archive->db);
if (lzma_err(SzArchiveOpen(&archive->stream.inStream,
&archive->db,
&archive->stream.allocImp,
&archive->stream.allocTempImp)) != SZ_OK)
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
return NULL; /* Error is set by lzma_err! */
} /* if */
len = archive->db.Database.NumFiles * sizeof (LZMAfile);
archive->files = (LZMAfile *) allocator.Malloc(len);
if (archive->files == NULL)
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
}
/*
* Init with 0 so we know when a folder is already cached
* Values will be set by LZMA_openRead()
*/
memset(archive->files, 0, len);
len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
archive->folders = (LZMAfolder *) allocator.Malloc(len);
if (archive->folders == NULL)
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
}
/*
* Init with 0 so we know when a folder is already cached
* Values will be set by LZMA_read()
*/
memset(archive->folders, 0, len);
if(!lzma_files_init(archive))
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL);
}
return(archive);
} /* LZMA_openArchive */
/*
* Moved to seperate function so we can use alloca then immediately throw
* away the allocated stack space...
*/
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
const char *odir, const char *str, size_t flen)
{
char *newstr = __PHYSFS_smallAlloc(flen + 1);
if (newstr == NULL)
return;
memcpy(newstr, str, flen);
newstr[flen] = '\0';
cb(callbackdata, odir, newstr);
__PHYSFS_smallFree(newstr);
} /* doEnumCallback */
static void LZMA_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
size_t dlen = strlen(dname),
dlen_inc = dlen + ((dlen > 0) ? 1 : 0);
LZMAarchive *archive = (LZMAarchive *) opaque;
LZMAfile *file = NULL,
*lastFile = &archive->files[archive->db.Database.NumFiles];
if (dlen)
{
file = lzma_find_file(archive, dname);
if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */
file += 1;
}
else
{
file = archive->files;
}
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, );
while (file < lastFile)
{
const char * fname = file->item->Name;
const char * dirNameEnd = fname + dlen_inc;
if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */
break;
if (strchr(dirNameEnd, '/')) /* Skip subdirs */
{
file++;
continue;
}
/* Do the actual callback... */
doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd));
file++;
}
} /* LZMA_enumerateFiles */
static int LZMA_exists(dvoid *opaque, const char *name)
{
LZMAarchive *archive = (LZMAarchive *) opaque;
return(lzma_find_file(archive, name) != NULL);
} /* LZMA_exists */
static PHYSFS_sint64 LZMA_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
LZMAarchive *archive = (LZMAarchive *) opaque;
LZMAfile *file = lzma_find_file(archive, name);
*fileExists = (file != NULL);
BAIL_IF_MACRO(file == NULL, NULL, -1);
BAIL_IF_MACRO(!file->item->IsLastWriteTimeDefined, NULL, -1); /* write-time may not be defined for every file */
return(lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime));
} /* LZMA_getLastModTime */
static int LZMA_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
LZMAarchive *archive = (LZMAarchive *) opaque;
LZMAfile *file = lzma_find_file(archive, name);
*fileExists = (file != NULL);
return(file == NULL ? 0 : file->item->IsDirectory);
} /* LZMA_isDirectory */
static int LZMA_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* LZMA_isSymLink */
static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
{
LZMAarchive *archive = (LZMAarchive *) opaque;
LZMAfile *file = lzma_find_file(archive, name);
*fileExists = (file != NULL);
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, NULL);
file->position = 0;
file->folder->references++; /* Increase refcount for automatic cleanup... */
return(file);
} /* LZMA_openRead */
static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* LZMA_openWrite */
static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* LZMA_openAppend */
static void LZMA_dirClose(dvoid *opaque)
{
LZMAarchive *archive = (LZMAarchive *) opaque;
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
{
LZMA_fileClose(&archive->files[fileIndex]);
} /* for */
SzArDbExFree(&archive->db, SzFreePhysicsFS);
__PHYSFS_platformClose(archive->stream.file);
lzma_archive_exit(archive);
} /* LZMA_dirClose */
static int LZMA_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* LZMA_remove */
static int LZMA_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* LZMA_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA =
{
"7Z",
LZMA_ARCHIVE_DESCRIPTION,
"Dennis Schridde <devurandom@gmx.net>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
{
&__PHYSFS_ArchiveInfo_LZMA,
LZMA_isArchive, /* isArchive() method */
LZMA_openArchive, /* openArchive() method */
LZMA_enumerateFiles, /* enumerateFiles() method */
LZMA_exists, /* exists() method */
LZMA_isDirectory, /* isDirectory() method */
LZMA_isSymLink, /* isSymLink() method */
LZMA_getLastModTime, /* getLastModTime() method */
LZMA_openRead, /* openRead() method */
LZMA_openWrite, /* openWrite() method */
LZMA_openAppend, /* openAppend() method */
LZMA_remove, /* remove() method */
LZMA_mkdir, /* mkdir() method */
LZMA_dirClose, /* dirClose() method */
LZMA_read, /* read() method */
LZMA_write, /* write() method */
LZMA_eof, /* eof() method */
LZMA_tell, /* tell() method */
LZMA_seek, /* seek() method */
LZMA_fileLength, /* fileLength() method */
LZMA_fileClose /* fileClose() method */
};
#endif /* defined PHYSFS_SUPPORTS_7Z */
/* end of lzma.c ... */
+471
View File
@@ -0,0 +1,471 @@
/*
* 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.txt in the source's root directory.
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_MVL)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(dvoid *opaque)
{
MVLinfo *info = ((MVLinfo *) opaque);
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
} /* MVL_dirClose */
static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
MVLfileinfo *finfo = (MVLfileinfo *) 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(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* MVL_write */
static int MVL_eof(fvoid *opaque)
{
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
MVLentry *entry = finfo->entry;
return(finfo->curPos >= entry->size);
} /* MVL_eof */
static PHYSFS_sint64 MVL_tell(fvoid *opaque)
{
return(((MVLfileinfo *) opaque)->curPos);
} /* MVL_tell */
static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
MVLfileinfo *finfo = (MVLfileinfo *) 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(fvoid *opaque)
{
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
return((PHYSFS_sint64) finfo->entry->size);
} /* MVL_fileLength */
static int MVL_fileClose(fvoid *opaque)
{
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
allocator.Free(finfo);
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)
{
if (one != two)
{
const MVLentry *a = (const MVLentry *) _a;
return(strcmp(a[one].name, a[two].name));
} /* if */
return 0;
} /* mvl_entry_cmp */
static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != 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));
} /* if */
} /* 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;
BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
info->entryCount = fileCount;
info->entries = (MVLentry *) allocator.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 void *MVL_openArchive(const char *name, int forWriting)
{
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
memset(info, '\0', sizeof (MVLinfo));
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
if (!mvl_load_entries(name, forWriting, info))
goto MVL_openArchive_failed;
strcpy(info->filename, name);
info->last_mod_time = modtime;
return(info);
MVL_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
allocator.Free(info->filename);
if (info->entries != NULL)
allocator.Free(info->entries);
allocator.Free(info);
} /* if */
return(NULL);
} /* MVL_openArchive */
static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
/* no directories in MVL files. */
if (*dname == '\0')
{
MVLinfo *info = ((MVLinfo *) opaque);
MVLentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
for (i = 0; i < max; i++, entry++)
cb(callbackdata, origdir, entry->name);
} /* if */
} /* 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_stricmpASCII(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(dvoid *opaque, const char *name)
{
return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL);
} /* MVL_exists */
static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = MVL_exists(opaque, name);
return(0); /* never directories in a groupfile. */
} /* MVL_isDirectory */
static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = MVL_exists(opaque, name);
return(0); /* never symlinks in a groupfile. */
} /* MVL_isSymLink */
static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
MVLinfo *info = ((MVLinfo *) opaque);
PHYSFS_sint64 retval = -1;
*fileExists = (mvl_find_entry(info, name) != NULL);
if (*fileExists) /* use time of MVL itself in the physical filesystem. */
retval = info->last_mod_time;
return(retval);
} /* MVL_getLastModTime */
static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
{
MVLinfo *info = ((MVLinfo *) opaque);
MVLfileinfo *finfo;
MVLentry *entry;
entry = mvl_find_entry(info, fnm);
*fileExists = (entry != NULL);
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if ( (finfo->handle == NULL) ||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
{
allocator.Free(finfo);
return(NULL);
} /* if */
finfo->curPos = 0;
finfo->entry = entry;
return(finfo);
} /* MVL_openRead */
static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MVL_openWrite */
static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MVL_openAppend */
static int MVL_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MVL_remove */
static int MVL_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MVL_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
{
"MVL",
MVL_ARCHIVE_DESCRIPTION,
"Bradley Bell <btb@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_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 */
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 */
};
#endif /* defined PHYSFS_SUPPORTS_MVL */
/* end of mvl.c ... */
+633
View File
@@ -0,0 +1,633 @@
/*
* QPAK support routines for PhysicsFS.
*
* This archiver handles the archive format utilized by Quake 1 and 2.
* Quake3-based games use the PkZip/Info-Zip format (which our zip.c
* archiver handles).
*
* ========================================================================
*
* This format info (in more detail) comes from:
* http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
*
* Quake PAK Format
*
* Header
* (4 bytes) signature = 'PACK'
* (4 bytes) directory offset
* (4 bytes) directory length
*
* Directory
* (56 bytes) file name
* (4 bytes) file position
* (4 bytes) file length
*
* ========================================================================
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_QPAK)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if 1 /* Make this case insensitive? */
#define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y)
#define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z)
#else
#define QPAK_strcmp(x, y) strcmp(x, y)
#define QPAK_strncmp(x, y, z) strncmp(x, y, z)
#endif
typedef struct
{
char name[56];
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
} QPAKentry;
typedef struct
{
char *filename;
PHYSFS_sint64 last_mod_time;
PHYSFS_uint32 entryCount;
QPAKentry *entries;
} QPAKinfo;
typedef struct
{
void *handle;
QPAKentry *entry;
PHYSFS_uint32 curPos;
} QPAKfileinfo;
/* Magic numbers... */
#define QPAK_SIG 0x4b434150 /* "PACK" in ASCII. */
static void QPAK_dirClose(dvoid *opaque)
{
QPAKinfo *info = ((QPAKinfo *) opaque);
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
} /* QPAK_dirClose */
static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
QPAKentry *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);
} /* QPAK_read */
static PHYSFS_sint64 QPAK_write(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* QPAK_write */
static int QPAK_eof(fvoid *opaque)
{
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
QPAKentry *entry = finfo->entry;
return(finfo->curPos >= entry->size);
} /* QPAK_eof */
static PHYSFS_sint64 QPAK_tell(fvoid *opaque)
{
return(((QPAKfileinfo *) opaque)->curPos);
} /* QPAK_tell */
static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
QPAKentry *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);
} /* QPAK_seek */
static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque)
{
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
return((PHYSFS_sint64) finfo->entry->size);
} /* QPAK_fileLength */
static int QPAK_fileClose(fvoid *opaque)
{
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
allocator.Free(finfo);
return(1);
} /* QPAK_fileClose */
static int qpak_open(const char *filename, int forWriting,
void **fh, PHYSFS_uint32 *count)
{
PHYSFS_uint32 buf;
*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, sizeof (PHYSFS_uint32), 1) != 1)
goto openQpak_failed;
buf = PHYSFS_swapULE32(buf);
GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed);
if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1)
goto openQpak_failed;
buf = PHYSFS_swapULE32(buf); /* directory table offset. */
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
goto openQpak_failed;
*count = PHYSFS_swapULE32(*count);
/* corrupted archive? */
GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed);
if (!__PHYSFS_platformSeek(*fh, buf))
goto openQpak_failed;
*count /= 64;
return(1);
openQpak_failed:
if (*fh != NULL)
__PHYSFS_platformClose(*fh);
*count = -1;
*fh = NULL;
return(0);
} /* qpak_open */
static int QPAK_isArchive(const char *filename, int forWriting)
{
void *fh;
PHYSFS_uint32 fileCount;
int retval = qpak_open(filename, forWriting, &fh, &fileCount);
if (fh != NULL)
__PHYSFS_platformClose(fh);
return(retval);
} /* QPAK_isArchive */
static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
const QPAKentry *a = (const QPAKentry *) _a;
return(QPAK_strcmp(a[one].name, a[two].name));
} /* if */
return 0;
} /* qpak_entry_cmp */
static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
QPAKentry tmp;
QPAKentry *first = &(((QPAKentry *) _a)[one]);
QPAKentry *second = &(((QPAKentry *) _a)[two]);
memcpy(&tmp, first, sizeof (QPAKentry));
memcpy(first, second, sizeof (QPAKentry));
memcpy(second, &tmp, sizeof (QPAKentry));
} /* if */
} /* qpak_entry_swap */
static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info)
{
void *fh = NULL;
PHYSFS_uint32 fileCount;
QPAKentry *entry;
BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0);
info->entryCount = fileCount;
info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount);
if (info->entries == NULL)
{
__PHYSFS_platformClose(fh);
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
} /* if */
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
{
PHYSFS_uint32 loc;
if (__PHYSFS_platformRead(fh,&entry->name,sizeof(entry->name),1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
if (__PHYSFS_platformRead(fh,&loc,sizeof(loc),1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
if (__PHYSFS_platformRead(fh,&entry->size,sizeof(entry->size),1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
entry->size = PHYSFS_swapULE32(entry->size);
entry->startPos = PHYSFS_swapULE32(loc);
} /* for */
__PHYSFS_platformClose(fh);
__PHYSFS_sort(info->entries, info->entryCount,
qpak_entry_cmp, qpak_entry_swap);
return(1);
} /* qpak_load_entries */
static void *QPAK_openArchive(const char *name, int forWriting)
{
QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo));
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
memset(info, '\0', sizeof (QPAKinfo));
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
if (info->filename == NULL)
{
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
goto QPAK_openArchive_failed;
} /* if */
if (!qpak_load_entries(name, forWriting, info))
goto QPAK_openArchive_failed;
strcpy(info->filename, name);
info->last_mod_time = modtime;
return(info);
QPAK_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
allocator.Free(info->filename);
if (info->entries != NULL)
allocator.Free(info->entries);
allocator.Free(info);
} /* if */
return(NULL);
} /* QPAK_openArchive */
static PHYSFS_sint32 qpak_find_start_of_dir(QPAKinfo *info, const char *path,
int stop_on_first_find)
{
PHYSFS_sint32 lo = 0;
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
PHYSFS_uint32 dlen = strlen(path);
PHYSFS_sint32 retval = -1;
const char *name;
int rc;
if (*path == '\0') /* root dir? */
return(0);
if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
dlen--;
while (lo <= hi)
{
middle = lo + ((hi - lo) / 2);
name = info->entries[middle].name;
rc = QPAK_strncmp(path, name, dlen);
if (rc == 0)
{
char ch = name[dlen];
if (ch < '/') /* make sure this isn't just a substr match. */
rc = -1;
else if (ch > '/')
rc = 1;
else
{
if (stop_on_first_find) /* Just checking dir's existance? */
return(middle);
if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
return(middle + 1);
/* there might be more entries earlier in the list. */
retval = middle;
hi = middle - 1;
} /* else */
} /* if */
if (rc > 0)
lo = middle + 1;
else
hi = middle - 1;
} /* while */
return(retval);
} /* qpak_find_start_of_dir */
/*
* Moved to seperate function so we can use alloca then immediately throw
* away the allocated stack space...
*/
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
const char *odir, const char *str, PHYSFS_sint32 ln)
{
char *newstr = __PHYSFS_smallAlloc(ln + 1);
if (newstr == NULL)
return;
memcpy(newstr, str, ln);
newstr[ln] = '\0';
cb(callbackdata, odir, newstr);
__PHYSFS_smallFree(newstr);
} /* doEnumCallback */
static void QPAK_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
QPAKinfo *info = ((QPAKinfo *) opaque);
PHYSFS_sint32 dlen, dlen_inc, max, i;
i = qpak_find_start_of_dir(info, dname, 0);
if (i == -1) /* no such directory. */
return;
dlen = strlen(dname);
if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
dlen--;
dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
max = (PHYSFS_sint32) info->entryCount;
while (i < max)
{
char *add;
char *ptr;
PHYSFS_sint32 ln;
char *e = info->entries[i].name;
if ((dlen) && ((QPAK_strncmp(e, dname, dlen)) || (e[dlen] != '/')))
break; /* past end of this dir; we're done. */
add = e + dlen_inc;
ptr = strchr(add, '/');
ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
doEnumCallback(cb, callbackdata, origdir, add, ln);
ln += dlen_inc; /* point past entry to children... */
/* increment counter and skip children of subdirs... */
while ((++i < max) && (ptr != NULL))
{
char *e_new = info->entries[i].name;
if ((QPAK_strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
break;
} /* while */
} /* while */
} /* QPAK_enumerateFiles */
/*
* This will find the QPAKentry associated with a path in platform-independent
* notation. Directories don't have QPAKentries associated with them, but
* (*isDir) will be set to non-zero if a dir was hit.
*/
static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir)
{
QPAKentry *a = info->entries;
PHYSFS_sint32 pathlen = strlen(path);
PHYSFS_sint32 lo = 0;
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
const char *thispath = NULL;
int rc;
while (lo <= hi)
{
middle = lo + ((hi - lo) / 2);
thispath = a[middle].name;
rc = QPAK_strncmp(path, thispath, pathlen);
if (rc > 0)
lo = middle + 1;
else if (rc < 0)
hi = middle - 1;
else /* substring match...might be dir or entry or nothing. */
{
if (isDir != NULL)
{
*isDir = (thispath[pathlen] == '/');
if (*isDir)
return(NULL);
} /* if */
if (thispath[pathlen] == '\0') /* found entry? */
return(&a[middle]);
/* adjust search params, try again. */
else if (thispath[pathlen] > '/')
hi = middle - 1;
else
lo = middle + 1;
} /* if */
} /* while */
if (isDir != NULL)
*isDir = 0;
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* qpak_find_entry */
static int QPAK_exists(dvoid *opaque, const char *name)
{
int isDir;
QPAKinfo *info = (QPAKinfo *) opaque;
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
return((entry != NULL) || (isDir));
} /* QPAK_exists */
static int QPAK_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
QPAKinfo *info = (QPAKinfo *) opaque;
int isDir;
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
*fileExists = ((isDir) || (entry != NULL));
if (isDir)
return(1); /* definitely a dir. */
BAIL_MACRO(ERR_NO_SUCH_FILE, 0);
} /* QPAK_isDirectory */
static int QPAK_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = QPAK_exists(opaque, name);
return(0); /* never symlinks in a quake pak. */
} /* QPAK_isSymLink */
static PHYSFS_sint64 QPAK_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
int isDir;
QPAKinfo *info = ((QPAKinfo *) opaque);
PHYSFS_sint64 retval = -1;
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
*fileExists = ((isDir) || (entry != NULL));
if (*fileExists) /* use time of QPAK itself in the physical filesystem. */
retval = info->last_mod_time;
return(retval);
} /* QPAK_getLastModTime */
static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
{
QPAKinfo *info = ((QPAKinfo *) opaque);
QPAKfileinfo *finfo;
QPAKentry *entry;
int isDir;
entry = qpak_find_entry(info, fnm, &isDir);
*fileExists = ((entry != NULL) || (isDir));
BAIL_IF_MACRO(isDir, ERR_NOT_A_FILE, NULL);
BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL);
finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo));
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if ( (finfo->handle == NULL) ||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
{
allocator.Free(finfo);
return(NULL);
} /* if */
finfo->curPos = 0;
finfo->entry = entry;
return(finfo);
} /* QPAK_openRead */
static fvoid *QPAK_openWrite(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* QPAK_openWrite */
static fvoid *QPAK_openAppend(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* QPAK_openAppend */
static int QPAK_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* QPAK_remove */
static int QPAK_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* QPAK_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK =
{
"PAK",
QPAK_ARCHIVE_DESCRIPTION,
"Ryan C. Gordon <icculus@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
{
&__PHYSFS_ArchiveInfo_QPAK,
QPAK_isArchive, /* isArchive() method */
QPAK_openArchive, /* openArchive() method */
QPAK_enumerateFiles, /* enumerateFiles() method */
QPAK_exists, /* exists() method */
QPAK_isDirectory, /* isDirectory() method */
QPAK_isSymLink, /* isSymLink() method */
QPAK_getLastModTime, /* getLastModTime() method */
QPAK_openRead, /* openRead() method */
QPAK_openWrite, /* openWrite() method */
QPAK_openAppend, /* openAppend() method */
QPAK_remove, /* remove() method */
QPAK_mkdir, /* mkdir() method */
QPAK_dirClose, /* dirClose() method */
QPAK_read, /* read() method */
QPAK_write, /* write() method */
QPAK_eof, /* eof() method */
QPAK_tell, /* tell() method */
QPAK_seek, /* seek() method */
QPAK_fileLength, /* fileLength() method */
QPAK_fileClose /* fileClose() method */
};
#endif /* defined PHYSFS_SUPPORTS_QPAK */
/* end of qpak.c ... */
+531
View File
@@ -0,0 +1,531 @@
/*
* WAD support routines for PhysicsFS.
*
* This driver handles DOOM engine archives ("wads").
* This format (but not this driver) was designed by id Software for use
* with the DOOM engine.
* The specs of the format are from the unofficial doom specs v1.666
* found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html
* The format of the archive: (from the specs)
*
* A WAD file has three parts:
* (1) a twelve-byte header
* (2) one or more "lumps"
* (3) a directory or "info table" that contains the names, offsets, and
* sizes of all the lumps in the WAD
*
* The header consists of three four-byte parts:
* (a) an ASCII string which must be either "IWAD" or "PWAD"
* (b) a 4-byte (long) integer which is the number of lumps in the wad
* (c) a long integer which is the file offset to the start of
* the directory
*
* The directory has one 16-byte entry for every lump. Each entry consists
* of three parts:
*
* (a) a long integer, the file offset to the start of the lump
* (b) a long integer, the size of the lump in bytes
* (c) an 8-byte ASCII string, the name of the lump, padded with zeros.
* For example, the "DEMO1" entry in hexadecimal would be
* (44 45 4D 4F 31 00 00 00)
*
* Note that there is no way to tell if an opened WAD archive is a
* IWAD or PWAD with this archiver.
* I couldn't think of a way to provide that information, without being too
* hacky.
* I don't think it's really that important though.
*
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Travis Wells, based on the GRP archiver by
* Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_WAD)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
char name[18];
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
} WADentry;
typedef struct
{
char *filename;
PHYSFS_sint64 last_mod_time;
PHYSFS_uint32 entryCount;
PHYSFS_uint32 entryOffset;
WADentry *entries;
} WADinfo;
typedef struct
{
void *handle;
WADentry *entry;
PHYSFS_uint32 curPos;
} WADfileinfo;
static void WAD_dirClose(dvoid *opaque)
{
WADinfo *info = ((WADinfo *) opaque);
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
} /* WAD_dirClose */
static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
WADfileinfo *finfo = (WADfileinfo *) opaque;
WADentry *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);
} /* WAD_read */
static PHYSFS_sint64 WAD_write(fvoid *opaque, const void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* WAD_write */
static int WAD_eof(fvoid *opaque)
{
WADfileinfo *finfo = (WADfileinfo *) opaque;
WADentry *entry = finfo->entry;
return(finfo->curPos >= entry->size);
} /* WAD_eof */
static PHYSFS_sint64 WAD_tell(fvoid *opaque)
{
return(((WADfileinfo *) opaque)->curPos);
} /* WAD_tell */
static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset)
{
WADfileinfo *finfo = (WADfileinfo *) opaque;
WADentry *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);
} /* WAD_seek */
static PHYSFS_sint64 WAD_fileLength(fvoid *opaque)
{
WADfileinfo *finfo = (WADfileinfo *) opaque;
return((PHYSFS_sint64) finfo->entry->size);
} /* WAD_fileLength */
static int WAD_fileClose(fvoid *opaque)
{
WADfileinfo *finfo = (WADfileinfo *) opaque;
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
allocator.Free(finfo);
return(1);
} /* WAD_fileClose */
static int wad_open(const char *filename, int forWriting,
void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset)
{
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 openWad_failed;
if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0)
{
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
goto openWad_failed;
} /* if */
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
goto openWad_failed;
*count = PHYSFS_swapULE32(*count);
if (__PHYSFS_platformRead(*fh, offset, sizeof (PHYSFS_uint32), 1) != 1)
goto openWad_failed;
*offset = PHYSFS_swapULE32(*offset);
return(1);
openWad_failed:
if (*fh != NULL)
__PHYSFS_platformClose(*fh);
*count = -1;
*fh = NULL;
return(0);
} /* wad_open */
static int WAD_isArchive(const char *filename, int forWriting)
{
void *fh;
PHYSFS_uint32 fileCount,offset;
int retval = wad_open(filename, forWriting, &fh, &fileCount,&offset);
if (fh != NULL)
__PHYSFS_platformClose(fh);
return(retval);
} /* WAD_isArchive */
static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
const WADentry *a = (const WADentry *) _a;
return(strcmp(a[one].name, a[two].name));
} /* if */
return 0;
} /* wad_entry_cmp */
static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
{
if (one != two)
{
WADentry tmp;
WADentry *first = &(((WADentry *) _a)[one]);
WADentry *second = &(((WADentry *) _a)[two]);
memcpy(&tmp, first, sizeof (WADentry));
memcpy(first, second, sizeof (WADentry));
memcpy(second, &tmp, sizeof (WADentry));
} /* if */
} /* wad_entry_swap */
static int wad_load_entries(const char *name, int forWriting, WADinfo *info)
{
void *fh = NULL;
PHYSFS_uint32 fileCount;
PHYSFS_uint32 directoryOffset;
WADentry *entry;
BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0);
info->entryCount = fileCount;
info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount);
if (info->entries == NULL)
{
__PHYSFS_platformClose(fh);
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
} /* if */
__PHYSFS_platformSeek(fh,directoryOffset);
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
{
if (__PHYSFS_platformRead(fh, &entry->startPos, 4, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
if (__PHYSFS_platformRead(fh, &entry->name, 8, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
entry->name[8] = '\0'; /* name might not be null-terminated in file. */
entry->size = PHYSFS_swapULE32(entry->size);
entry->startPos = PHYSFS_swapULE32(entry->startPos);
} /* for */
__PHYSFS_platformClose(fh);
__PHYSFS_sort(info->entries, info->entryCount,
wad_entry_cmp, wad_entry_swap);
return(1);
} /* wad_load_entries */
static void *WAD_openArchive(const char *name, int forWriting)
{
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo));
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
memset(info, '\0', sizeof (WADinfo));
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed);
if (!wad_load_entries(name, forWriting, info))
goto WAD_openArchive_failed;
strcpy(info->filename, name);
info->last_mod_time = modtime;
return(info);
WAD_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
allocator.Free(info->filename);
if (info->entries != NULL)
allocator.Free(info->entries);
allocator.Free(info);
} /* if */
return(NULL);
} /* WAD_openArchive */
static void WAD_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
{
WADinfo *info = ((WADinfo *) opaque);
WADentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
const char *name;
char *sep;
if (*dname == '\0') /* root directory enumeration? */
{
for (i = 0; i < max; i++, entry++)
{
name = entry->name;
if (strchr(name, '/') == NULL)
cb(callbackdata, origdir, name);
} /* for */
} /* if */
else
{
for (i = 0; i < max; i++, entry++)
{
name = entry->name;
sep = strchr(name, '/');
if (sep != NULL)
{
if (strncmp(dname, name, (sep - name)) == 0)
cb(callbackdata, origdir, sep + 1);
} /* if */
} /* for */
} /* else */
} /* WAD_enumerateFiles */
static WADentry *wad_find_entry(WADinfo *info, const char *name)
{
WADentry *a = info->entries;
PHYSFS_sint32 lo = 0;
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
int rc;
while (lo <= hi)
{
middle = lo + ((hi - lo) / 2);
rc = strcmp(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);
} /* wad_find_entry */
static int WAD_exists(dvoid *opaque, const char *name)
{
return(wad_find_entry(((WADinfo *) opaque), name) != NULL);
} /* WAD_exists */
static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists)
{
WADentry *entry = wad_find_entry(((WADinfo *) opaque), name);
if (entry != NULL)
{
char *n;
*fileExists = 1;
/* Can't be a directory if it's a subdirectory. */
if (strchr(entry->name, '/') != NULL)
return(0);
/* Check if it matches "MAP??" or "E?M?" ... */
n = entry->name;
if ((n[0] == 'E' && n[2] == 'M') ||
(n[0] == 'M' && n[1] == 'A' && n[2] == 'P' && n[6] == 0))
{
return(1);
} /* if */
return(0);
} /* if */
else
{
*fileExists = 0;
return(0);
} /* else */
} /* WAD_isDirectory */
static int WAD_isSymLink(dvoid *opaque, const char *name, int *fileExists)
{
*fileExists = WAD_exists(opaque, name);
return(0); /* never symlinks in a wad. */
} /* WAD_isSymLink */
static PHYSFS_sint64 WAD_getLastModTime(dvoid *opaque,
const char *name,
int *fileExists)
{
WADinfo *info = ((WADinfo *) opaque);
PHYSFS_sint64 retval = -1;
*fileExists = (wad_find_entry(info, name) != NULL);
if (*fileExists) /* use time of WAD itself in the physical filesystem. */
retval = info->last_mod_time;
return(retval);
} /* WAD_getLastModTime */
static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists)
{
WADinfo *info = ((WADinfo *) opaque);
WADfileinfo *finfo;
WADentry *entry;
entry = wad_find_entry(info, fnm);
*fileExists = (entry != NULL);
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo));
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if ( (finfo->handle == NULL) ||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
{
allocator.Free(finfo);
return(NULL);
} /* if */
finfo->curPos = 0;
finfo->entry = entry;
return(finfo);
} /* WAD_openRead */
static fvoid *WAD_openWrite(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* WAD_openWrite */
static fvoid *WAD_openAppend(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* WAD_openAppend */
static int WAD_remove(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* WAD_remove */
static int WAD_mkdir(dvoid *opaque, const char *name)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* WAD_mkdir */
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD =
{
"WAD",
WAD_ARCHIVE_DESCRIPTION,
"Travis Wells <traviswells@mchsi.com>",
"http://www.3dmm2.com/doom/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
{
&__PHYSFS_ArchiveInfo_WAD,
WAD_isArchive, /* isArchive() method */
WAD_openArchive, /* openArchive() method */
WAD_enumerateFiles, /* enumerateFiles() method */
WAD_exists, /* exists() method */
WAD_isDirectory, /* isDirectory() method */
WAD_isSymLink, /* isSymLink() method */
WAD_getLastModTime, /* getLastModTime() method */
WAD_openRead, /* openRead() method */
WAD_openWrite, /* openWrite() method */
WAD_openAppend, /* openAppend() method */
WAD_remove, /* remove() method */
WAD_mkdir, /* mkdir() method */
WAD_dirClose, /* dirClose() method */
WAD_read, /* read() method */
WAD_write, /* write() method */
WAD_eof, /* eof() method */
WAD_tell, /* tell() method */
WAD_seek, /* seek() method */
WAD_fileLength, /* fileLength() method */
WAD_fileClose /* fileClose() method */
};
#endif /* defined PHYSFS_SUPPORTS_WAD */
/* end of wad.c ... */
File diff suppressed because it is too large Load Diff