view dmpackutil.c @ 0:32250b436bca

Initial re-import.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 28 Sep 2012 01:54:23 +0300
parents
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;
}