Whoops, forgot to add the file. :)
This commit is contained in:
parent
6d42436270
commit
8c3501491c
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* 7zip support routines for PhysicsFS.
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file was written by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
#if PHYSFS_SUPPORTS_7Z
|
||||
|
||||
#include "physfs_lzmasdk.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ISeekInStream seekStream; /* lzma sdk i/o interface (lower level). */
|
||||
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
|
||||
CLookToRead lookStream; /* lzma sdk i/o interface (higher level). */
|
||||
} SZIPLookToRead;
|
||||
|
||||
/* One SZIPentry is kept for each file in an open 7zip archive. */
|
||||
typedef struct
|
||||
{
|
||||
__PHYSFS_DirTreeEntry tree; /* manages directory tree */
|
||||
PHYSFS_uint32 dbidx; /* index into lzma sdk database */
|
||||
} SZIPentry;
|
||||
|
||||
/* One SZIPinfo is kept for each open 7zip archive. */
|
||||
typedef struct
|
||||
{
|
||||
__PHYSFS_DirTree tree; /* manages directory tree. */
|
||||
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
|
||||
CSzArEx db; /* lzma sdk archive database object. */
|
||||
} SZIPinfo;
|
||||
|
||||
|
||||
static PHYSFS_ErrorCode szipErrorCode(const SRes rc)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
case SZ_OK: return PHYSFS_ERR_OK;
|
||||
case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT;
|
||||
case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY;
|
||||
case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT;
|
||||
case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED;
|
||||
case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT;
|
||||
case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO;
|
||||
case SZ_ERROR_READ: return PHYSFS_ERR_IO;
|
||||
case SZ_ERROR_WRITE: return PHYSFS_ERR_IO;
|
||||
case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT;
|
||||
case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED;
|
||||
default: break;
|
||||
} /* switch */
|
||||
|
||||
return PHYSFS_ERR_OTHER_ERROR;
|
||||
} /* szipErrorCode */
|
||||
|
||||
|
||||
/* LZMA SDK's ISzAlloc interface ... */
|
||||
|
||||
static void *SZIP_ISzAlloc_Alloc(void *p, size_t size)
|
||||
{
|
||||
return allocator.Malloc(size ? size : 1);
|
||||
} /* SZIP_ISzAlloc_Alloc */
|
||||
|
||||
static void SZIP_ISzAlloc_Free(void *p, void *address)
|
||||
{
|
||||
if (address)
|
||||
allocator.Free(address);
|
||||
} /* SZIP_ISzAlloc_Free */
|
||||
|
||||
static ISzAlloc SZIP_SzAlloc = {
|
||||
SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free
|
||||
};
|
||||
|
||||
|
||||
/* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead,
|
||||
which implements the higher-level ILookInStream on top of that, handling
|
||||
buffering and such for us. */
|
||||
|
||||
/* LZMA SDK's ISeekInStream interface ... */
|
||||
|
||||
static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size)
|
||||
{
|
||||
SZIPLookToRead *stream = (SZIPLookToRead *) p;
|
||||
PHYSFS_Io *io = stream->io;
|
||||
const PHYSFS_uint64 len = (PHYSFS_uint64) *size;
|
||||
const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
*size = 0;
|
||||
return SZ_ERROR_READ;
|
||||
} /* if */
|
||||
|
||||
*size = (size_t) rc;
|
||||
return SZ_OK;
|
||||
} /* SZIP_ISeekInStream_Read */
|
||||
|
||||
static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
SZIPLookToRead *stream = (SZIPLookToRead *) p;
|
||||
PHYSFS_Io *io = stream->io;
|
||||
PHYSFS_sint64 base;
|
||||
PHYSFS_uint64 newpos;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SZ_SEEK_SET:
|
||||
base = 0;
|
||||
break;
|
||||
|
||||
case SZ_SEEK_CUR:
|
||||
base = io->tell(io);
|
||||
break;
|
||||
|
||||
case SZ_SEEK_END:
|
||||
base = io->length(io);
|
||||
break;
|
||||
|
||||
default:
|
||||
return SZ_ERROR_FAIL;
|
||||
} /* switch */
|
||||
|
||||
if (base < 0)
|
||||
return SZ_ERROR_FAIL;
|
||||
else if ((*pos < 0) && (((Int64) base) < -*pos))
|
||||
return SZ_ERROR_FAIL;
|
||||
|
||||
newpos = (PHYSFS_uint64) (((Int64) base) + *pos);
|
||||
if (!io->seek(io, newpos))
|
||||
return SZ_ERROR_FAIL;
|
||||
|
||||
*pos = (Int64) newpos;
|
||||
return SZ_OK;
|
||||
} /* SZIP_ISeekInStream_Seek */
|
||||
|
||||
|
||||
static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io)
|
||||
{
|
||||
stream->seekStream.Read = SZIP_ISeekInStream_Read;
|
||||
stream->seekStream.Seek = SZIP_ISeekInStream_Seek;
|
||||
|
||||
stream->io = io;
|
||||
|
||||
/* !!! FIXME: can we use lookahead? Is there value to it? */
|
||||
LookToRead_Init(&stream->lookStream);
|
||||
LookToRead_CreateVTable(&stream->lookStream, False);
|
||||
stream->lookStream.realStream = &stream->seekStream;
|
||||
} /* szipInitStream */
|
||||
|
||||
|
||||
/* Do this in a separate function so we can smallAlloc without looping. */
|
||||
static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx)
|
||||
{
|
||||
const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL);
|
||||
const size_t utf16buflen = utf16len * 2;
|
||||
PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen);
|
||||
const size_t utf8buflen = utf16len * 4;
|
||||
char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen);
|
||||
int retval = 0;
|
||||
|
||||
if (utf16 && utf8)
|
||||
{
|
||||
const int isdir = SzArEx_IsDir(&info->db, idx) != 0;
|
||||
SZIPentry *entry;
|
||||
SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16);
|
||||
PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen);
|
||||
entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir);
|
||||
retval = (entry != NULL);
|
||||
if (retval)
|
||||
entry->dbidx = idx;
|
||||
} /* if */
|
||||
|
||||
__PHYSFS_smallFree(utf8);
|
||||
__PHYSFS_smallFree(utf16);
|
||||
|
||||
return retval;
|
||||
} /* szipLoadEntry */
|
||||
|
||||
|
||||
static int szipLoadEntries(SZIPinfo *info)
|
||||
{
|
||||
const PHYSFS_uint32 count = info->db.NumFiles;
|
||||
int retval = 0;
|
||||
|
||||
if (__PHYSFS_DirTreeInit(&info->tree, count, sizeof (SZIPentry)))
|
||||
{
|
||||
PHYSFS_uint32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0);
|
||||
retval = 1;
|
||||
} /* if */
|
||||
|
||||
return retval;
|
||||
} /* szipLoadEntries */
|
||||
|
||||
|
||||
static void SZIP_closeArchive(void *opaque)
|
||||
{
|
||||
SZIPinfo *info = (SZIPinfo *) opaque;
|
||||
if (info)
|
||||
{
|
||||
SzArEx_Free(&info->db, &SZIP_SzAlloc);
|
||||
__PHYSFS_DirTreeDeinit(&info->tree);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
} /* SZIP_closeArchive */
|
||||
|
||||
|
||||
static void *SZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
|
||||
{
|
||||
SZIPLookToRead stream;
|
||||
ISzAlloc *alloc = &SZIP_SzAlloc;
|
||||
SZIPinfo *info = NULL;
|
||||
SRes rc;
|
||||
|
||||
/* !!! FIXME: this is a race condition; we need a global init method that gets called when registering new archivers. */
|
||||
static int generatedTable = 0;
|
||||
if (!generatedTable)
|
||||
{
|
||||
generatedTable = 1;
|
||||
CrcGenerateTable();
|
||||
} /* if */
|
||||
|
||||
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
|
||||
|
||||
info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo));
|
||||
BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
||||
memset(info, '\0', sizeof (*info));
|
||||
|
||||
SzArEx_Init(&info->db);
|
||||
|
||||
info->io = io;
|
||||
|
||||
szipInitStream(&stream, io);
|
||||
rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc);
|
||||
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed);
|
||||
|
||||
GOTO_IF_ERRPASS(!szipLoadEntries(info), failed);
|
||||
|
||||
return info;
|
||||
|
||||
failed:
|
||||
info->io = NULL; /* don't let cleanup destroy the PHYSFS_Io. */
|
||||
SZIP_closeArchive(info);
|
||||
return NULL;
|
||||
} /* SZIP_openArchive */
|
||||
|
||||
|
||||
static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path)
|
||||
{
|
||||
/* !!! FIXME: the current lzma sdk C API only allows you to decompress
|
||||
!!! FIXME: the entire file at once, which isn't ideal. Fix this in the
|
||||
!!! FIXME: SDK and then convert this all to a streaming interface. */
|
||||
|
||||
SZIPinfo *info = (SZIPinfo *) opaque;
|
||||
SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
|
||||
ISzAlloc *alloc = &SZIP_SzAlloc;
|
||||
SZIPLookToRead stream;
|
||||
PHYSFS_Io *retval = NULL;
|
||||
PHYSFS_Io *io = NULL;
|
||||
UInt32 blockIndex = 0xFFFFFFFF;
|
||||
Byte *outBuffer = NULL;
|
||||
size_t outBufferSize = 0;
|
||||
size_t offset = 0;
|
||||
size_t outSizeProcessed = 0;
|
||||
void *buf = NULL;
|
||||
SRes rc;
|
||||
|
||||
BAIL_IF_ERRPASS(!entry, NULL);
|
||||
BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
|
||||
|
||||
io = info->io->duplicate(info->io);
|
||||
GOTO_IF_ERRPASS(!io, SZIP_openRead_failed);
|
||||
|
||||
szipInitStream(&stream, io);
|
||||
|
||||
rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx,
|
||||
&blockIndex, &outBuffer, &outBufferSize, &offset,
|
||||
&outSizeProcessed, alloc, alloc);
|
||||
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed);
|
||||
|
||||
io->destroy(io);
|
||||
io = NULL;
|
||||
|
||||
buf = allocator.Malloc(outSizeProcessed);
|
||||
GOTO_IF(rc != SZ_OK, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed);
|
||||
memcpy(buf, outBuffer + offset, outSizeProcessed);
|
||||
|
||||
alloc->Free(alloc, outBuffer);
|
||||
outBuffer = NULL;
|
||||
|
||||
retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free);
|
||||
GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed);
|
||||
|
||||
return retval;
|
||||
|
||||
SZIP_openRead_failed:
|
||||
if (io != NULL)
|
||||
io->destroy(io);
|
||||
|
||||
if (buf)
|
||||
allocator.Free(buf);
|
||||
|
||||
if (outBuffer)
|
||||
alloc->Free(alloc, outBuffer);
|
||||
|
||||
return NULL;
|
||||
} /* SZIP_openRead */
|
||||
|
||||
|
||||
static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename)
|
||||
{
|
||||
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
|
||||
} /* SZIP_openWrite */
|
||||
|
||||
|
||||
static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename)
|
||||
{
|
||||
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
|
||||
} /* SZIP_openAppend */
|
||||
|
||||
|
||||
static int SZIP_remove(void *opaque, const char *name)
|
||||
{
|
||||
BAIL(PHYSFS_ERR_READ_ONLY, 0);
|
||||
} /* SZIP_remove */
|
||||
|
||||
|
||||
static int SZIP_mkdir(void *opaque, const char *name)
|
||||
{
|
||||
BAIL(PHYSFS_ERR_READ_ONLY, 0);
|
||||
} /* SZIP_mkdir */
|
||||
|
||||
|
||||
static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t)
|
||||
{
|
||||
const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000);
|
||||
const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000);
|
||||
const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low;
|
||||
return (quad - winEpochToUnixEpoch) / nanosecToMillisec;
|
||||
} /* lzmasdkTimeToPhysfsTime */
|
||||
|
||||
|
||||
static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
|
||||
{
|
||||
SZIPinfo *info = (SZIPinfo *) opaque;
|
||||
SZIPentry *entry;
|
||||
PHYSFS_uint32 idx;
|
||||
const CNtfsFileTime *ctimeval = NULL;
|
||||
const CNtfsFileTime *mtimeval = NULL;
|
||||
|
||||
entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
|
||||
BAIL_IF_ERRPASS(!entry, 0);
|
||||
idx = entry->dbidx;
|
||||
|
||||
if (entry->tree.isdir)
|
||||
{
|
||||
stat->filesize = -1;
|
||||
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
|
||||
} /* if */
|
||||
else
|
||||
{
|
||||
stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx);
|
||||
stat->filetype = PHYSFS_FILETYPE_REGULAR;
|
||||
} /* else */
|
||||
|
||||
if (info->db.MTime.Vals != NULL)
|
||||
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
|
||||
else if (info->db.CTime.Vals != NULL)
|
||||
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
|
||||
else
|
||||
stat->modtime = -1;
|
||||
|
||||
if (info->db.CTime.Vals != NULL)
|
||||
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
|
||||
else if (info->db.MTime.Vals != NULL)
|
||||
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
|
||||
else
|
||||
stat->createtime = -1;
|
||||
|
||||
stat->accesstime = -1;
|
||||
stat->readonly = 1;
|
||||
|
||||
return 1;
|
||||
} /* SZIP_stat */
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_SZIP =
|
||||
{
|
||||
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
|
||||
{
|
||||
"7Z",
|
||||
"7zip archives",
|
||||
"Ryan C. Gordon <icculus@icculus.org>",
|
||||
"https://icculus.org/physfs/",
|
||||
0, /* supportsSymlinks */
|
||||
},
|
||||
SZIP_openArchive,
|
||||
__PHYSFS_DirTreeEnumerateFiles,
|
||||
SZIP_openRead,
|
||||
SZIP_openWrite,
|
||||
SZIP_openAppend,
|
||||
SZIP_remove,
|
||||
SZIP_mkdir,
|
||||
SZIP_stat,
|
||||
SZIP_closeArchive
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_7Z */
|
||||
|
||||
/* end of archiver_7z.c ... */
|
||||
|
Loading…
Reference in New Issue