Added PHYSFS_mountHandle(). Now you can do archives-in-archives!
This commit is contained in:
parent
d9e98704f7
commit
84c6e2242e
143
src/physfs.c
143
src/physfs.c
|
@ -451,6 +451,127 @@ createMemoryIo_failed:
|
|||
} /* __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 ... */
|
||||
|
||||
|
@ -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);
|
||||
|
|
54
src/physfs.h
54
src/physfs.h
|
@ -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. */
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 == '\"')
|
||||
|
@ -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>" },
|
||||
|
|
Loading…
Reference in New Issue