Skip to content

Commit

Permalink
Added PHYSFS_mountHandle(). Now you can do archives-in-archives!
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Aug 30, 2010
1 parent d9e9870 commit 84c6e22
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 5 deletions.
143 changes: 143 additions & 0 deletions src/physfs.c
Expand Up @@ -451,6 +451,127 @@ PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
} /* __PHYSFS_createMemoryIo */


/* PHYSFS_Io implementation for i/o to a PHYSFS_File... */

static PHYSFS_sint64 handleIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
{
return PHYSFS_readBytes((PHYSFS_File *) io->opaque, buf, len);
} /* handleIo_read */

static PHYSFS_sint64 handleIo_write(PHYSFS_Io *io, const void *buffer,
PHYSFS_uint64 len)
{
return PHYSFS_writeBytes((PHYSFS_File *) io->opaque, buffer, len);
} /* handleIo_write */

static int handleIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
{
return PHYSFS_seek((PHYSFS_File *) io->opaque, offset);
} /* handleIo_seek */

static PHYSFS_sint64 handleIo_tell(PHYSFS_Io *io)
{
return PHYSFS_tell((PHYSFS_File *) io->opaque);
} /* handleIo_tell */

static PHYSFS_sint64 handleIo_length(PHYSFS_Io *io)
{
return PHYSFS_fileLength((PHYSFS_File *) io->opaque);
} /* handleIo_length */

static PHYSFS_Io *handleIo_duplicate(PHYSFS_Io *io)
{
/*
* There's no duplicate at the PHYSFS_File level, so we break the
* abstraction. We're allowed to: we're physfs.c!
*/
FileHandle *origfh = (FileHandle *) io->opaque;
FileHandle *newfh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
PHYSFS_Io *retval = NULL;

GOTO_IF_MACRO(newfh == NULL, ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
memset(newfh, '\0', sizeof (*newfh));

retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, handleIo_dupe_failed);

#if 0 /* we don't buffer the duplicate, at least not at the moment. */
if (origfh->buffer != NULL)
{
newfh->buffer = (PHYSFS_uint8 *) allocator.Malloc(origfh->bufsize);
GOTO_IF_MACRO(!newfh->buffer, ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
newfh->bufsize = origfh->bufsize;
} /* if */
#endif

newfh->io = origfh->io->duplicate(origfh->io);
GOTO_IF_MACRO(newfh->io == NULL, NULL, handleIo_dupe_failed);

newfh->forReading = origfh->forReading;
newfh->dirHandle = origfh->dirHandle;

__PHYSFS_platformGrabMutex(stateLock);
if (newfh->forReading)
{
newfh->next = openReadList;
openReadList = newfh;
} /* if */
else
{
newfh->next = openWriteList;
openWriteList = newfh;
} /* else */
__PHYSFS_platformReleaseMutex(stateLock);

memcpy(retval, io, sizeof (PHYSFS_Io));
retval->opaque = newfh;
return retval;

handleIo_dupe_failed:
if (newfh)
{
if (newfh->io != NULL) newfh->io->destroy(newfh->io);
if (newfh->buffer != NULL) allocator.Free(newfh->buffer);
allocator.Free(newfh);
} /* if */

return NULL;
} /* handleIo_duplicate */

static int handleIo_flush(PHYSFS_Io *io)
{
return PHYSFS_flush((PHYSFS_File *) io->opaque);
} /* handleIo_flush */

static void handleIo_destroy(PHYSFS_Io *io)
{
if (io->opaque != NULL)
PHYSFS_close((PHYSFS_File *) io->opaque);
allocator.Free(io);
} /* handleIo_destroy */

static const PHYSFS_Io __PHYSFS_handleIoInterface =
{
handleIo_read,
handleIo_write,
handleIo_seek,
handleIo_tell,
handleIo_length,
handleIo_duplicate,
handleIo_flush,
handleIo_destroy,
NULL
};

static PHYSFS_Io *__PHYSFS_createHandleIo(PHYSFS_File *f)
{
PHYSFS_Io *io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
BAIL_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, NULL);
memcpy(io, &__PHYSFS_handleIoInterface, sizeof (*io));
io->opaque = f;
return io;
} /* __PHYSFS_createHandleIo */


/* functions ... */

Expand Down Expand Up @@ -1356,6 +1477,28 @@ int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *),
} /* PHYSFS_mountMemory */


int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
const char *mountPoint, int appendToPath)
{
int retval = 0;
PHYSFS_Io *io = NULL;

BAIL_IF_MACRO(file == NULL, ERR_INVALID_ARGUMENT, 0);

io = __PHYSFS_createHandleIo(file);
BAIL_IF_MACRO(io == NULL, NULL, 0);
retval = doMount(io, fname, mountPoint, appendToPath);
if (!retval)
{
/* docs say not to destruct in case of failure, so cheat. */
io->opaque = NULL;
io->destroy(io);
} /* if */

return retval;
} /* PHYSFS_mountHandle */


int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
{
BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
Expand Down
54 changes: 54 additions & 0 deletions src/physfs.h
Expand Up @@ -2989,6 +2989,60 @@ PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len,
const char *mountPoint, int appendToPath);


/**
* \fn int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname, const char *mountPoint, int appendToPath)
* \brief Add an archive, contained in a PHYSFS_File handle, to the search path.
*
* \warning Unless you have some special, low-level need, you should be using
* PHYSFS_mount() instead of this.
*
* \warning Archives-in-archives may be very slow! While a PHYSFS_File can
* seek even when the data is compressed, it may do so by rewinding
* to the start and decompressing everything before the seek point.
* Normal archive usage may do a lot of seeking behind the scenes.
* As such, you might find normal archive usage extremely painful
* if mounted this way. Plan accordingly: if you, say, have a
* self-extracting .zip file, and want to mount something in it,
* compress the contents of the inner archive and make sure the outer
* .zip file doesn't compress the inner archive too.
*
* This function operates just like PHYSFS_mount(), but takes a PHYSFS_File
* handle instead of a pathname. This handle contains all the data of the
* archive, and is used instead of a real file in the physical filesystem.
* The PHYSFS_File may be backed by a real file in the physical filesystem,
* but isn't necessarily. The most popular use for this is likely to mount
* archives stored inside other archives.
*
* (filename) is only used here to optimize archiver selection (if you name it
* XXXXX.zip, we might try the ZIP archiver first, for example). It doesn't
* need to refer to a real file at all, and can even be NULL. If the filename
* isn't helpful, the system will try every archiver until one works or none
* of them do.
*
* (file) must remain until the archive is unmounted. When the archive is
* unmounted, the system will call PHYSFS_close(file). If you need this
* handle to survive, you will have to wrap this in a PHYSFS_Io and use
* PHYSFS_mountIo() instead.
*
* If this function fails, PHYSFS_close(file) is not called.
*
* \param file The PHYSFS_File handle containing archive data.
* \param fname Filename that can represent this stream. Can be NULL.
* \param mountPoint Location in the interpolated tree that this archive
* will be "mounted", in platform-independent notation.
* NULL or "" is equivalent to "/".
* \param appendToPath nonzero to append to search path, zero to prepend.
* \return nonzero if added to path, zero on failure (bogus archive, etc).
* Specifics of the error can be gleaned from
* PHYSFS_getLastError().
*
* \sa PHYSFS_unmount
* \sa PHYSFS_getSearchPath
* \sa PHYSFS_getMountPoint
*/
PHYSFS_DECL int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
const char *mountPoint, int appendToPath);

/* Everything above this line is part of the PhysicsFS 2.1 API. */


Expand Down
38 changes: 33 additions & 5 deletions test/test_physfs.c
Expand Up @@ -134,8 +134,14 @@ static void freeBuf(void *buf)
free(buf);
} /* freeBuf */

typedef enum
{
MNTTYPE_PATH,
MNTTYPE_MEMORY,
MNTTYPE_HANDLE
} MountType;

static int cmd_mount_internal(char *args, const int fromMem)
static int cmd_mount_internal(char *args, const MountType mnttype)
{
char *ptr;
char *mntpoint = NULL;
Expand Down Expand Up @@ -180,9 +186,24 @@ static int cmd_mount_internal(char *args, const int fromMem)

/*printf("[%s], [%s], [%d]\n", args, mntpoint, appending);*/

if (!fromMem)
if (mnttype == MNTTYPE_PATH)
rc = PHYSFS_mount(args, mntpoint, appending);
else

else if (mnttype == MNTTYPE_HANDLE)
{
PHYSFS_File *f = PHYSFS_openRead(args);
if (f == NULL)
{
printf("PHYSFS_openRead('%s') failed. reason: %s.\n", args, PHYSFS_getLastError());
return 1;
} /* if */

rc = PHYSFS_mountHandle(f, args, mntpoint, appending);
if (!rc)
PHYSFS_close(f);
} /* else if */

else if (mnttype == MNTTYPE_MEMORY)
{
FILE *in = fopen(args, "rb");
void *buf = NULL;
Expand Down Expand Up @@ -233,16 +254,22 @@ static int cmd_mount_internal(char *args, const int fromMem)

static int cmd_mount(char *args)
{
return cmd_mount_internal(args, 0);
return cmd_mount_internal(args, MNTTYPE_PATH);
} /* cmd_mount */


static int cmd_mount_mem(char *args)
{
return cmd_mount_internal(args, 1);
return cmd_mount_internal(args, MNTTYPE_MEMORY);
} /* cmd_mount_mem */


static int cmd_mount_handle(char *args)
{
return cmd_mount_internal(args, MNTTYPE_HANDLE);
} /* cmd_mount_handle */


static int cmd_removearchive(char *args)
{
if (*args == '\"')
Expand Down Expand Up @@ -1086,6 +1113,7 @@ static const command_info commands[] =
{ "addarchive", cmd_addarchive, 2, "<archiveLocation> <append>" },
{ "mount", cmd_mount, 3, "<archiveLocation> <mntpoint> <append>" },
{ "mountmem", cmd_mount_mem, 3, "<archiveLocation> <mntpoint> <append>" },
{ "mounthandle", cmd_mount_handle, 3, "<archiveLocation> <mntpoint> <append>" },
{ "removearchive", cmd_removearchive, 1, "<archiveLocation>" },
{ "unmount", cmd_removearchive, 1, "<archiveLocation>" },
{ "enumerate", cmd_enumerate, 1, "<dirToEnumerate>" },
Expand Down

0 comments on commit 84c6e22

Please sign in to comment.