Added PHYSFS_mountMemory().
This commit is contained in:
parent
8cd320b858
commit
bb9f5e5049
209
src/physfs.c
209
src/physfs.c
|
@ -267,6 +267,191 @@ createNativeIo_failed:
|
|||
} /* __PHYSFS_createNativeIo */
|
||||
|
||||
|
||||
/* PHYSFS_Io implementation for i/o to a memory buffer... */
|
||||
|
||||
typedef struct __PHYSFS_MemoryIoInfo
|
||||
{
|
||||
const PHYSFS_uint8 *buf;
|
||||
PHYSFS_uint64 len;
|
||||
PHYSFS_uint64 pos;
|
||||
PHYSFS_Io *parent;
|
||||
volatile PHYSFS_uint32 refcount;
|
||||
void (*destruct)(void *);
|
||||
} MemoryIoInfo;
|
||||
|
||||
static PHYSFS_sint64 memoryIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
|
||||
{
|
||||
MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
const PHYSFS_uint64 avail = info->len - info->pos;
|
||||
assert(avail <= info->len);
|
||||
|
||||
if (avail == 0)
|
||||
return 0; /* we're at EOF; nothing to do. */
|
||||
|
||||
if (len > avail)
|
||||
len = avail;
|
||||
|
||||
memcpy(buf, info->buf + info->pos, len);
|
||||
info->pos += len;
|
||||
return len;
|
||||
} /* memoryIo_read */
|
||||
|
||||
static PHYSFS_sint64 memoryIo_write(PHYSFS_Io *io, const void *buffer,
|
||||
PHYSFS_uint64 len)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* memoryIo_write */
|
||||
|
||||
static int memoryIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
|
||||
{
|
||||
MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
BAIL_IF_MACRO(offset > info->len, ERR_PAST_EOF, 0);
|
||||
info->pos = offset;
|
||||
return 1;
|
||||
} /* memoryIo_seek */
|
||||
|
||||
static PHYSFS_sint64 memoryIo_tell(PHYSFS_Io *io)
|
||||
{
|
||||
const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
return (PHYSFS_sint64) info->pos;
|
||||
} /* memoryIo_tell */
|
||||
|
||||
static PHYSFS_sint64 memoryIo_length(PHYSFS_Io *io)
|
||||
{
|
||||
const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
return (PHYSFS_sint64) info->len;
|
||||
} /* memoryIo_length */
|
||||
|
||||
static PHYSFS_Io *memoryIo_duplicate(PHYSFS_Io *io)
|
||||
{
|
||||
MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
MemoryIoInfo *newinfo = NULL;
|
||||
PHYSFS_Io *parent = info->parent;
|
||||
PHYSFS_Io *retval = NULL;
|
||||
|
||||
/* avoid deep copies. */
|
||||
assert((!parent) || (!((MemoryIoInfo *) parent->opaque)->parent) );
|
||||
|
||||
/* share the buffer between duplicates. */
|
||||
if (parent != NULL) /* dup the parent, increment its refcount. */
|
||||
return parent->duplicate(parent);
|
||||
|
||||
/* we're the parent. */
|
||||
|
||||
retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
|
||||
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
newinfo = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
|
||||
if (!newinfo)
|
||||
{
|
||||
allocator.Free(retval);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
|
||||
} /* if */
|
||||
|
||||
/* !!! FIXME: want lockless atomic increment. */
|
||||
__PHYSFS_platformGrabMutex(stateLock);
|
||||
info->refcount++;
|
||||
__PHYSFS_platformReleaseMutex(stateLock);
|
||||
|
||||
memset(newinfo, '\0', sizeof (*info));
|
||||
newinfo->buf = info->buf;
|
||||
newinfo->len = info->len;
|
||||
newinfo->pos = 0;
|
||||
newinfo->parent = io;
|
||||
newinfo->refcount = 0;
|
||||
newinfo->destruct = NULL;
|
||||
|
||||
memcpy(retval, io, sizeof (*retval));
|
||||
retval->opaque = newinfo;
|
||||
return retval;
|
||||
} /* memoryIo_duplicate */
|
||||
|
||||
static int memoryIo_flush(PHYSFS_Io *io) { return 1; /* it's read-only. */ }
|
||||
|
||||
static void memoryIo_destroy(PHYSFS_Io *io)
|
||||
{
|
||||
MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
PHYSFS_Io *parent = info->parent;
|
||||
int should_die = 0;
|
||||
|
||||
if (parent != NULL)
|
||||
{
|
||||
assert(info->buf == ((MemoryIoInfo *) info->parent->opaque)->buf);
|
||||
assert(info->len == ((MemoryIoInfo *) info->parent->opaque)->len);
|
||||
assert(info->refcount == 0);
|
||||
assert(info->destruct == NULL);
|
||||
allocator.Free(info);
|
||||
allocator.Free(io);
|
||||
parent->destroy(parent); /* decrements refcount. */
|
||||
return;
|
||||
} /* if */
|
||||
|
||||
/* we _are_ the parent. */
|
||||
assert(info->refcount > 0); /* even in a race, we hold a reference. */
|
||||
|
||||
/* !!! FIXME: want lockless atomic decrement. */
|
||||
__PHYSFS_platformGrabMutex(stateLock);
|
||||
info->refcount--;
|
||||
should_die = (info->refcount == 0);
|
||||
__PHYSFS_platformReleaseMutex(stateLock);
|
||||
|
||||
if (should_die)
|
||||
{
|
||||
void (*destruct)(void *) = info->destruct;
|
||||
void *buf = (void *) info->buf;
|
||||
io->opaque = NULL; /* kill this here in case of race. */
|
||||
destruct = info->destruct;
|
||||
allocator.Free(info);
|
||||
allocator.Free(io);
|
||||
if (destruct != NULL)
|
||||
destruct(buf);
|
||||
} /* if */
|
||||
} /* memoryIo_destroy */
|
||||
|
||||
|
||||
static const PHYSFS_Io __PHYSFS_memoryIoInterface =
|
||||
{
|
||||
memoryIo_read,
|
||||
memoryIo_write,
|
||||
memoryIo_seek,
|
||||
memoryIo_tell,
|
||||
memoryIo_length,
|
||||
memoryIo_duplicate,
|
||||
memoryIo_flush,
|
||||
memoryIo_destroy,
|
||||
NULL
|
||||
};
|
||||
|
||||
PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
|
||||
void (*destruct)(void *))
|
||||
{
|
||||
PHYSFS_Io *io = NULL;
|
||||
MemoryIoInfo *info = NULL;
|
||||
|
||||
io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
|
||||
GOTO_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, createMemoryIo_failed);
|
||||
info = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
|
||||
GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, createMemoryIo_failed);
|
||||
|
||||
memset(info, '\0', sizeof (*info));
|
||||
info->buf = (const PHYSFS_uint8 *) buf;
|
||||
info->len = len;
|
||||
info->pos = 0;
|
||||
info->parent = NULL;
|
||||
info->refcount = 1;
|
||||
info->destruct = destruct;
|
||||
|
||||
memcpy(io, &__PHYSFS_memoryIoInterface, sizeof (*io));
|
||||
io->opaque = info;
|
||||
return io;
|
||||
|
||||
createMemoryIo_failed:
|
||||
if (info != NULL) allocator.Free(info);
|
||||
if (io != NULL) allocator.Free(io);
|
||||
return NULL;
|
||||
} /* __PHYSFS_createMemoryIo */
|
||||
|
||||
|
||||
|
||||
/* functions ... */
|
||||
|
||||
typedef struct
|
||||
|
@ -1147,6 +1332,30 @@ int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
|
|||
} /* PHYSFS_mountIo */
|
||||
|
||||
|
||||
int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *),
|
||||
const char *fname, const char *mountPoint,
|
||||
int appendToPath)
|
||||
{
|
||||
int retval = 0;
|
||||
PHYSFS_Io *io = NULL;
|
||||
|
||||
BAIL_IF_MACRO(buf == NULL, ERR_INVALID_ARGUMENT, 0);
|
||||
|
||||
io = __PHYSFS_createMemoryIo(buf, len, del);
|
||||
BAIL_IF_MACRO(io == NULL, NULL, 0);
|
||||
retval = doMount(io, fname, mountPoint, appendToPath);
|
||||
if (!retval)
|
||||
{
|
||||
/* docs say not to call (del) in case of failure, so cheat. */
|
||||
MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
|
||||
info->destruct = NULL;
|
||||
io->destroy(io);
|
||||
} /* if */
|
||||
|
||||
return retval;
|
||||
} /* PHYSFS_mountMemory */
|
||||
|
||||
|
||||
int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
|
||||
{
|
||||
BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
|
||||
|
|
47
src/physfs.h
47
src/physfs.h
|
@ -2937,6 +2937,53 @@ typedef struct PHYSFS_Io
|
|||
PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
|
||||
const char *mountPoint, int appendToPath);
|
||||
|
||||
|
||||
/**
|
||||
* \fn int PHYSFS_mountMemory(const void *ptr, PHYSFS_uint64 len, void (*del)(void *), const char *fname, const char *mountPoint, int appendToPath)
|
||||
* \brief Add an archive, contained in a memory buffer, to the search path.
|
||||
*
|
||||
* \warning Unless you have some special, low-level need, you should be using
|
||||
* PHYSFS_mount() instead of this.
|
||||
*
|
||||
* This function operates just like PHYSFS_mount(), but takes a memory buffer
|
||||
* instead of a pathname. This buffer contains all the data of the archive,
|
||||
* and is used instead of a real file in the physical filesystem.
|
||||
*
|
||||
* (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.
|
||||
*
|
||||
* (ptr) must remain until the archive is unmounted. When the archive is
|
||||
* unmounted, the system will call (del)(ptr), which will notify you that
|
||||
* the system is done with the buffer, and give you a chance to free your
|
||||
* resources. (del) can be NULL, in which case the system will make no
|
||||
* attempt to free the buffer.
|
||||
*
|
||||
* If this function fails, (del) is not called.
|
||||
*
|
||||
* \param ptr Address of the memory buffer containing the archive data.
|
||||
* \param len Size of memory buffer, in bytes.
|
||||
* \param del A callback that triggers upon unmount. Can be NULL.
|
||||
* \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_mountMemory(const void *buf, PHYSFS_uint64 len,
|
||||
void (*del)(void *), const char *fname,
|
||||
const char *mountPoint, int appendToPath);
|
||||
|
||||
|
||||
/* Everything above this line is part of the PhysicsFS 2.1 API. */
|
||||
|
||||
|
||||
|
|
|
@ -1033,6 +1033,14 @@ extern PHYSFS_Allocator __PHYSFS_AllocatorHooks;
|
|||
*/
|
||||
PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode);
|
||||
|
||||
/*
|
||||
* Create a PHYSFS_Io for a buffer of memory (READ-ONLY). If you already
|
||||
* have one of these, just use its duplicate() method, and it'll increment
|
||||
* its refcount without allocating a copy of the buffer.
|
||||
*/
|
||||
PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
|
||||
void (*destruct)(void *));
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
|
|
@ -128,11 +128,19 @@ static int cmd_addarchive(char *args)
|
|||
} /* cmd_addarchive */
|
||||
|
||||
|
||||
static int cmd_mount(char *args)
|
||||
/* wrap free() to avoid calling convention wankery. */
|
||||
static void freeBuf(void *buf)
|
||||
{
|
||||
free(buf);
|
||||
} /* freeBuf */
|
||||
|
||||
|
||||
static int cmd_mount_internal(char *args, const int fromMem)
|
||||
{
|
||||
char *ptr;
|
||||
char *mntpoint = NULL;
|
||||
int appending = 0;
|
||||
int rc = 0;
|
||||
|
||||
if (*args == '\"')
|
||||
{
|
||||
|
@ -172,15 +180,69 @@ static int cmd_mount(char *args)
|
|||
|
||||
/*printf("[%s], [%s], [%d]\n", args, mntpoint, appending);*/
|
||||
|
||||
if (PHYSFS_mount(args, mntpoint, appending))
|
||||
if (!fromMem)
|
||||
rc = PHYSFS_mount(args, mntpoint, appending);
|
||||
else
|
||||
{
|
||||
FILE *in = fopen(args, "rb");
|
||||
void *buf = NULL;
|
||||
long len = 0;
|
||||
|
||||
if (in == NULL)
|
||||
{
|
||||
printf("Failed to open %s to read into memory: %s.\n", args, strerror(errno));
|
||||
return 1;
|
||||
} /* if */
|
||||
|
||||
if ( (fseek(in, 0, SEEK_END) != 0) || ((len = ftell(in)) < 0) )
|
||||
{
|
||||
printf("Failed to find size of %s to read into memory: %s.\n", args, strerror(errno));
|
||||
fclose(in);
|
||||
return 1;
|
||||
} /* if */
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL)
|
||||
{
|
||||
printf("Failed to allocate space to read %s into memory: %s.\n", args, strerror(errno));
|
||||
fclose(in);
|
||||
return 1;
|
||||
} /* if */
|
||||
|
||||
if ((fseek(in, 0, SEEK_SET) != 0) || (fread(buf, len, 1, in) != 1))
|
||||
{
|
||||
printf("Failed to read %s into memory: %s.\n", args, strerror(errno));
|
||||
fclose(in);
|
||||
free(buf);
|
||||
return 1;
|
||||
} /* if */
|
||||
|
||||
fclose(in);
|
||||
|
||||
rc = PHYSFS_mountMemory(buf, len, freeBuf, args, mntpoint, appending);
|
||||
} /* else */
|
||||
|
||||
if (rc)
|
||||
printf("Successful.\n");
|
||||
else
|
||||
printf("Failure. reason: %s.\n", PHYSFS_getLastError());
|
||||
|
||||
return 1;
|
||||
} /* cmd_mount_internal */
|
||||
|
||||
|
||||
static int cmd_mount(char *args)
|
||||
{
|
||||
return cmd_mount_internal(args, 0);
|
||||
} /* cmd_mount */
|
||||
|
||||
|
||||
static int cmd_mount_mem(char *args)
|
||||
{
|
||||
return cmd_mount_internal(args, 1);
|
||||
} /* cmd_mount_mem */
|
||||
|
||||
|
||||
static int cmd_removearchive(char *args)
|
||||
{
|
||||
if (*args == '\"')
|
||||
|
@ -1023,6 +1085,7 @@ static const command_info commands[] =
|
|||
{ "deinit", cmd_deinit, 0, NULL },
|
||||
{ "addarchive", cmd_addarchive, 2, "<archiveLocation> <append>" },
|
||||
{ "mount", cmd_mount, 3, "<archiveLocation> <mntpoint> <append>" },
|
||||
{ "mountmem", cmd_mount_mem, 3, "<archiveLocation> <mntpoint> <append>" },
|
||||
{ "removearchive", cmd_removearchive, 1, "<archiveLocation>" },
|
||||
{ "unmount", cmd_removearchive, 1, "<archiveLocation>" },
|
||||
{ "enumerate", cmd_enumerate, 1, "<dirToEnumerate>" },
|
||||
|
|
Loading…
Reference in New Issue