view dmpackutil.c @ 96:6bf5220fa47e

Urgh .. use memset to silence some bogus GCC warnings about using potentially uninitialized values, while that will not actually be possible. In any case, it is annoying.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 02 Oct 2012 18:52:28 +0300
parents 32250b436bca
children 09528946f92b bb14d7907eb2
line wrap: on
line source

/*
 * DMLib
 * -- PACK-file additional utility routines
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2011 Tecnic Software productions (TNSP)
 */
#include "dmpackutil.h"
#include <zlib.h>
#include "dmfile.h"


DMPackEntry *dm_pack_entry_copy(const DMPackEntry *src)
{
    DMPackEntry *node = dm_pack_entry_new();
    if (node == NULL)
        return NULL;

    strncpy(node->filename, src->filename, sizeof(node->filename));
    node->filename[sizeof(node->filename) - 1] = 0;

    node->size     = src->size;
    node->offset   = src->offset;
    node->length   = src->length;
    node->resFlags = src->resFlags;

    return node;
}


/*
 * CLOSE/WRITE the packfile
 */
int dm_pack_write(DMPackFile * pack)
{
    DMPackEntry *node;
    DMPackFileHeader hdr;

    if (pack == NULL)
        return DMERR_OK;

    if (pack->file == NULL)
        return DMERR_FOPEN;

    // Compute directory offset and number of entries
    memcpy(&hdr.ident, DPACK_IDENT, sizeof(hdr.ident));
    hdr.version = DPACK_VERSION;
    hdr.dirEntries = 0;
    hdr.dirOffset =
        sizeof(hdr.ident) + sizeof(hdr.version) +
        sizeof(hdr.dirEntries) + sizeof(hdr.dirOffset);

    node = pack->entries;
    while (node != NULL)
    {
        hdr.dirEntries++;
        hdr.dirOffset += node->length;
        node = node->next;
    }

    // Write PACK header
    if (fseek(pack->file, 0L, SEEK_SET) != 0)
        return -4;

    dm_fwrite_str(pack->file, (Uint8 *) & hdr.ident, sizeof(hdr.ident));
    dm_fwrite_le16(pack->file, hdr.version);
    dm_fwrite_le32(pack->file, hdr.dirEntries);
    dm_fwrite_le32(pack->file, hdr.dirOffset);

    // Write the directory
    if (fseek(pack->file, hdr.dirOffset, SEEK_SET) != 0)
        return -5;

    node = pack->entries;
    while (node != NULL)
    {
        // Write one entry
        dm_fwrite_str(pack->file, node->filename, sizeof(node->filename));
        dm_fwrite_le32(pack->file, node->size);
        dm_fwrite_le32(pack->file, node->offset);
        dm_fwrite_le32(pack->file, node->length);
        dm_fwrite_le32(pack->file, node->resFlags);

        node = node->next;
    }

    return DMERR_OK;
}


/*
 * CREATE a packfile, for writing
 */
int dm_pack_create(const char *filename, DMPackFile ** pack)
{
    // Allocate packfile-structure
    *pack = (DMPackFile *) dmCalloc(1, sizeof(DMPackFile));
    if (*pack == NULL)
        return DMERR_MALLOC;

    // Open the file
    (*pack)->file = fopen(filename, "wb");
    if ((*pack)->file == NULL)
    {
        dmFree(*pack);
        return DMERR_FOPEN;
    }

    (*pack)->filename = dm_strdup(filename);

    // Set the result
    return DMERR_OK;
}


/*
 * ADD a file into the PACK
 */
int dm_pack_add_file(DMPackFile * pack, const char *filename, BOOL doCompress, int resFlags,
                    DMPackEntry ** ppEntry)
{
    z_stream compStream;
    off_t startOffs;
    unsigned int compSize;
    FILE *inFile;
    Uint8 *inBuffer, *outBuffer;
    DMPackEntry entry, *node;
    int result;

    *ppEntry = NULL;

    if (pack == NULL)
        return DMERR_OK;

    if (pack->file == NULL)
        return DMERR_FOPEN;

    // Compute starting offset
    startOffs = sizeof(DMPackFileHeader);
    node = pack->entries;
    while (node != NULL)
    {
        startOffs += node->length;
        node = node->next;
    }

    // Seek to the position
    if (fseek(pack->file, startOffs, SEEK_SET) != 0)
        return DMERR_INVALID;

    // Read file data
    if ((inFile = fopen(filename, "rb")) == NULL)
        return -1;

    // Allocate temporary buffer
    inBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE);
    if (!inBuffer)
    {
        fclose(inFile);
        return DMERR_MALLOC;
    }

    outBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE);
    if (!outBuffer)
    {
        dmFree(inBuffer);
        fclose(inFile);
        return DMERR_MALLOC;
    }

    // Read (and possibly compress) the data
    compSize = 0;
    compStream.zalloc = (alloc_func) Z_NULL;
    compStream.zfree = (free_func) Z_NULL;
    compStream.opaque = (voidpf) Z_NULL;
    result = deflateInit(&compStream, (doCompress) ? Z_DEFAULT_COMPRESSION : 0);
    if (result != Z_OK)
    {
        dmFree(inBuffer);
        dmFree(outBuffer);
        fclose(inFile);
        return DMERR_COMPRESSION;
    }

    // Initialize compression streams
    result = Z_OK;
    while (!feof(inFile) && result == Z_OK)
    {
        compStream.avail_in = fread(inBuffer, sizeof(Uint8), DPACK_TMPSIZE, inFile);
        compStream.next_in = inBuffer;
        compStream.next_out = outBuffer;
        compStream.avail_out = DPACK_TMPSIZE;
        compStream.total_out = 0;
        result = deflate(&compStream, Z_FULL_FLUSH);

        if (result == Z_OK && compStream.total_out > 0)
        {
            compSize += compStream.total_out;
            fwrite(outBuffer, sizeof(Uint8), compStream.total_out, pack->file);
        }
    }

    // Create directory entry
    strncpy(entry.filename, filename, sizeof(entry.filename));
    entry.filename[sizeof(entry.filename) - 1] = 0;
    entry.size = compStream.total_in;
    entry.offset = startOffs;
    entry.length = compSize;
    entry.resFlags = resFlags;

    // Cleanup
    deflateEnd(&compStream);
    dmFree(inBuffer);
    dmFree(outBuffer);
    fclose(inFile);

    // Add directory entry
    *ppEntry = dm_pack_entry_copy(&entry);
    if (*ppEntry == NULL)
        return DMERR_MALLOC;

    dm_pack_entry_insert(&pack->entries, *ppEntry);

    return DMERR_OK;
}


/*
 * EXTRACT a file from the PACK
 */
int dm_pack_extract_file(DMPackFile *pack, DMPackEntry * entry)
{
    z_stream compStream;
    FILE *outFile;
    Uint8 *inBuffer, *outBuffer;
    size_t inDataLeft;
    int ret;

    if (pack == NULL)
        return DMERR_OK;

    if (pack->file == NULL)
        return DMERR_FOPEN;

    // Seek to the position
    if (fseek(pack->file, entry->offset, SEEK_SET) != 0)
        return DMERR_INVALID;

    // Open destination file
    if ((outFile = fopen(entry->filename, "wb")) == NULL)
        return -1;

    // Allocate temporary buffer
    if ((inBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE)) == NULL)
    {
        fclose(outFile);
        return DMERR_MALLOC;
    }

    if ((outBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE)) == NULL)
    {
        dmFree(inBuffer);
        fclose(outFile);
        return DMERR_MALLOC;
    }

    // Read and uncompress the data
    compStream.zalloc = (alloc_func) Z_NULL;
    compStream.zfree = (free_func) Z_NULL;
    compStream.opaque = (voidpf) Z_NULL;
    ret = inflateInit(&compStream);
    if (ret != Z_OK)
    {
        dmFree(inBuffer);
        dmFree(outBuffer);
        fclose(outFile);
        return DMERR_COMPRESSION;
    }

    // Initialize compression streams
    inDataLeft = entry->length;
    ret = Z_OK;
    while (inDataLeft > 0 && ret == Z_OK)
    {
        if (inDataLeft >= DPACK_TMPSIZE)
            compStream.avail_in = fread(inBuffer, sizeof(Uint8), DPACK_TMPSIZE, pack->file);
        else
            compStream.avail_in = fread(inBuffer, sizeof(Uint8), inDataLeft, pack->file);

        inDataLeft -= compStream.avail_in;
        compStream.next_in = inBuffer;

        while (compStream.avail_in > 0 && ret == Z_OK)
        {
            compStream.next_out = outBuffer;
            compStream.avail_out = DPACK_TMPSIZE;
            compStream.total_out = 0;
            ret = inflate(&compStream, Z_FULL_FLUSH);
            if (compStream.total_out > 0)
            {
                fwrite(outBuffer, sizeof(Uint8), compStream.total_out, outFile);
            }
        }
    }

    // Cleanup
    inflateEnd(&compStream);
    dmFree(inBuffer);
    dmFree(outBuffer);
    fclose(outFile);

    return DMERR_OK;
}