MegaGlest/mk/linux/mojosetup/archive_uz2.c

351 lines
9.7 KiB
C

/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#include "fileio.h"
#include "platform.h"
#if !SUPPORT_UZ2
MojoArchive *MojoArchive_createUZ2(MojoInput *io) { return NULL; }
#else
// UZ2 format is a simple compressed file format used by UnrealEngine2.
// it's just a stream of blocks like this:
// uint32 compressed size
// uint32 uncompressed size
// uint8 data[compressed size] <-- unpacks to (uncompressed size) bytes.
// Decompression is handled by zlib's "uncompress" function.
#include "zlib/zlib.h"
#define MAXCOMPSIZE 32768
#define MAXUNCOMPSIZE 33096 // MAXCOMPSIZE + 1%
// MojoInput implementation...
// Decompression is handled in the parent MojoInput, so this just needs to
// make sure we stay within the bounds of the tarfile entry.
typedef struct UZ2input
{
MojoInput *io;
int64 fsize;
uint64 position;
uint32 compsize;
uint8 compbuf[MAXCOMPSIZE];
uint32 uncompsize;
uint8 uncompbuf[MAXUNCOMPSIZE];
uint32 uncompindex;
} UZ2input;
typedef struct UZ2info
{
char *outname;
int64 outsize;
boolean enumerated;
} UZ2info;
static boolean unpack(UZ2input *inp)
{
MojoInput *io = inp->io;
uLongf ul = (uLongf) inp->uncompsize;
// we checked these formally elsewhere.
assert(inp->compsize > 0);
assert(inp->uncompsize > 0);
assert(inp->compsize <= MAXCOMPSIZE);
assert(inp->uncompsize <= MAXUNCOMPSIZE);
if (io->read(io, inp->compbuf, inp->compsize) != inp->compsize)
return false;
if (uncompress(inp->uncompbuf, &ul, inp->compbuf, inp->compsize) != Z_OK)
return false;
if (ul != ((uLongf) inp->uncompsize)) // corrupt data.
return false;
inp->uncompindex = 0;
return true;
} // unpack
static boolean MojoInput_uz2_ready(MojoInput *io)
{
UZ2input *input = (UZ2input *) io->opaque;
if (input->uncompsize > 0)
return true;
return true; // !!! FIXME: need to know we have a full compressed block.
} // MojoInput_uz2_ready
static int64 MojoInput_uz2_read(MojoInput *io, void *_buf, uint32 bufsize)
{
uint8 *buf = (uint8 *) _buf;
UZ2input *input = (UZ2input *) io->opaque;
int64 retval = 0;
while (bufsize > 0)
{
const uint32 available = input->uncompsize - input->uncompindex;
const uint32 cpy = (available < bufsize) ? available : bufsize;
if (available == 0)
{
if (input->position == input->fsize)
return 0;
else if (!MojoInput_readui32(input->io, &input->compsize))
return (retval == 0) ? -1 : retval;
else if (!MojoInput_readui32(input->io, &input->uncompsize))
return (retval == 0) ? -1 : retval;
else if (!unpack(input))
return (retval == 0) ? -1 : retval;
continue; // try again.
} // if
memcpy(buf, input->uncompbuf + input->uncompindex, cpy);
buf += cpy;
bufsize -= cpy;
retval += cpy;
input->uncompindex += cpy;
input->position += cpy;
} // while
return retval;
} // MojoInput_uz2_read
static boolean MojoInput_uz2_seek(MojoInput *io, uint64 pos)
{
UZ2input *input = (UZ2input *) io->opaque;
int64 seekpos = 0;
// in a perfect world, this wouldn't seek from the start if moving
// forward. But oh well.
input->position = 0;
while (input->position < pos)
{
if (!input->io->seek(input->io, seekpos))
return false;
else if (!MojoInput_readui32(io, &input->compsize))
return false;
else if (!MojoInput_readui32(io, &input->uncompsize))
return false;
// we checked these formally elsewhere.
assert(input->compsize > 0);
assert(input->uncompsize > 0);
assert(input->compsize <= MAXCOMPSIZE);
assert(input->uncompsize <= MAXUNCOMPSIZE);
input->position += input->uncompsize;
seekpos += (sizeof (uint32) * 2) + input->compsize;
} // while
// we are positioned on the compressed block that contains the seek target.
if (!unpack(input))
return false;
input->position -= input->uncompsize;
input->uncompindex = (uint32) (pos - input->position);
input->position += input->uncompindex;
return true;
} // MojoInput_uz2_seek
static int64 MojoInput_uz2_tell(MojoInput *io)
{
return (int64) (((UZ2input *) io->opaque)->position);
} // MojoInput_uz2_tell
static int64 MojoInput_uz2_length(MojoInput *io)
{
return ((UZ2input *) io->opaque)->fsize;
} // MojoInput_uz2_length
static MojoInput *MojoInput_uz2_duplicate(MojoInput *io)
{
MojoInput *retval = NULL;
UZ2input *input = (UZ2input *) io->opaque;
MojoInput *newio = input->io->duplicate(input->io);
if (newio != NULL)
{
UZ2input *newopaque = (UZ2input *) xmalloc(sizeof (UZ2input));
newopaque->io = newio;
newopaque->fsize = input->fsize;
// everything else is properly zero'd by xmalloc().
retval = (MojoInput *) xmalloc(sizeof (MojoInput));
memcpy(retval, io, sizeof (MojoInput));
retval->opaque = newopaque;
} // if
return retval;
} // MojoInput_uz2_duplicate
static void MojoInput_uz2_close(MojoInput *io)
{
UZ2input *input = (UZ2input *) io->opaque;
input->io->close(input->io);
free(input);
free(io);
} // MojoInput_uz2_close
// MojoArchive implementation...
static boolean MojoArchive_uz2_enumerate(MojoArchive *ar)
{
UZ2info *info = (UZ2info *) ar->opaque;
MojoArchive_resetEntry(&ar->prevEnum);
info->enumerated = false;
return true;
} // MojoArchive_uz2_enumerate
static const MojoArchiveEntry *MojoArchive_uz2_enumNext(MojoArchive *ar)
{
UZ2info *info = (UZ2info *) ar->opaque;
MojoArchive_resetEntry(&ar->prevEnum);
if (info->enumerated)
return NULL; // only one file in this "archive".
ar->prevEnum.perms = MojoPlatform_defaultFilePerms();
ar->prevEnum.filesize = info->outsize;
ar->prevEnum.filename = xstrdup(info->outname);
ar->prevEnum.type = MOJOARCHIVE_ENTRY_FILE;
info->enumerated = true;
return &ar->prevEnum;
} // MojoArchive_uz2_enumNext
static MojoInput *MojoArchive_uz2_openCurrentEntry(MojoArchive *ar)
{
UZ2info *info = (UZ2info *) ar->opaque;
MojoInput *io = NULL;
UZ2input *opaque = NULL;
MojoInput *dupio = NULL;
if (!info->enumerated)
return NULL;
dupio = ar->io->duplicate(ar->io);
if (dupio == NULL)
return NULL;
opaque = (UZ2input *) xmalloc(sizeof (UZ2input));
opaque->io = dupio;
opaque->fsize = info->outsize;
// rest is zero'd by xmalloc().
io = (MojoInput *) xmalloc(sizeof (MojoInput));
io->ready = MojoInput_uz2_ready;
io->read = MojoInput_uz2_read;
io->seek = MojoInput_uz2_seek;
io->tell = MojoInput_uz2_tell;
io->length = MojoInput_uz2_length;
io->duplicate = MojoInput_uz2_duplicate;
io->close = MojoInput_uz2_close;
io->opaque = opaque;
return io;
} // MojoArchive_uz2_openCurrentEntry
static void MojoArchive_uz2_close(MojoArchive *ar)
{
UZ2info *info = (UZ2info *) ar->opaque;
MojoArchive_resetEntry(&ar->prevEnum);
ar->io->close(ar->io);
free(info->outname);
free(info);
free(ar);
} // MojoArchive_uz2_close
// Unfortunately, we have to walk the whole file, but we don't have to actually
// do any decompression work here. Just seek, read 8 bytes, repeat until EOF.
static int64 calculate_uz2_outsize(MojoInput *io)
{
int64 retval = 0;
uint32 compsize = 0;
uint32 uncompsize = 0;
int64 pos = 0;
if (!io->seek(io, 0))
return -1;
while (MojoInput_readui32(io, &compsize))
{
if (!MojoInput_readui32(io, &uncompsize))
return -1;
else if ((compsize > MAXCOMPSIZE) || (uncompsize > MAXUNCOMPSIZE))
return -1;
else if ((compsize == 0) || (uncompsize == 0))
return -1;
retval += uncompsize;
pos += (sizeof (uint32) * 2) + compsize;
if (!io->seek(io, pos))
return -1;
} // while
if (!io->seek(io, 0)) // make sure we're back to the start.
return -1;
return retval;
} // calculate_uz2_outsize
MojoArchive *MojoArchive_createUZ2(MojoInput *io, const char *origfname)
{
MojoArchive *ar = NULL;
const char *fname = NULL;
char *outname = NULL;
size_t len = 0;
int64 outsize = 0;
UZ2info *uz2info = NULL;
// There's no magic in a UZ2 that allows us to identify the format.
// The higher-level won't call this unless the file extension in
// (origfname) is ".uz2"
// Figure out the output name ("x.uz2" would produce "x").
if (origfname == NULL)
return NULL; // just in case.
fname = strrchr(origfname, '/');
if (fname == NULL)
fname = origfname;
else
fname++;
len = strlen(fname) - 4; // -4 == ".uz2"
if (strcasecmp(fname + len, ".uz2") != 0)
return NULL; // just in case.
outsize = calculate_uz2_outsize(io);
if (outsize < 0)
return NULL; // wasn't really a uz2? Corrupt/truncated file?
outname = (char *) xmalloc(len+1);
memcpy(outname, fname, len);
outname[len] = '\0';
uz2info = (UZ2info *) xmalloc(sizeof (UZ2info));
uz2info->enumerated = false;
uz2info->outname = outname;
uz2info->outsize = outsize;
ar = (MojoArchive *) xmalloc(sizeof (MojoArchive));
ar->opaque = uz2info;
ar->enumerate = MojoArchive_uz2_enumerate;
ar->enumNext = MojoArchive_uz2_enumNext;
ar->openCurrentEntry = MojoArchive_uz2_openCurrentEntry;
ar->close = MojoArchive_uz2_close;
ar->io = io;
return ar;
} // MojoArchive_createUZ2
#endif // SUPPORT_UZ2
// end of archive_uz2.c ...