/*
 * DMLib
 * -- PACK-file handling
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2011 Tecnic Software productions (TNSP)
 */
#include "dmpack.h"
#include "dmfile.h"


DMPackEntry *dmPackEntryNew()
{
    return (DMPackEntry *) dmMalloc0(sizeof(DMPackEntry));
}


void dmPackEntryFree(DMPackEntry * node)
{
    dmFree(node);
}


void dmPackEntryInsert(DMPackEntry ** packDir, DMPackEntry * node)
{
    if (*packDir != NULL)
    {
        node->prev = (*packDir)->prev;
        (*packDir)->prev->next = node;
        (*packDir)->prev = node;
    }
    else
    {
        *packDir = node->prev = node;
    }
    
    node->next = NULL;
}


void dmPackEntryDelete(DMPackEntry ** packDir, DMPackEntry * node)
{
    if (node->prev)
        node->prev->next = node->next;

    if (node->next)
        node->next->prev = node->prev;
    else
        (*packDir)->prev = node->prev;

    node->prev = node->next = NULL;
}


DMPackEntry *dmPackFind(DMPackEntry *list, const char *filename)
{
    DMPackEntry *node;

    for (node = list; node != NULL; node = node->next)
    {
        if (strcmp(node->filename, filename) == 0)
            return node;
    }

    return NULL;
}


/*
 * OPEN a packfile
 */
int dmPackOpen(const char *filename, DMPackFile ** ppPack, BOOL readOnly)
{
    unsigned int i;
    DMPackFile *pack;
    DMPackFileHeader hdr;

    *ppPack = NULL;

    // Allocate packfile-structure
    pack = (DMPackFile *) dmMalloc0(sizeof(DMPackFile));
    if (pack == NULL)
        return DMERR_MALLOC;

    // Open the file
    pack->file = fopen(filename, readOnly ? "rb" : "r+b");
    if (pack->file == NULL)
    {
        dmFree(pack);
        return DMERR_FOPEN;
    }

    pack->filename = dm_strdup(filename);

    // Read PACK header
    fseek(pack->file, 0L, SEEK_SET);
    if (!dm_fread_str(pack->file, (Uint8 *) &hdr.ident, sizeof(hdr.ident)) ||
        !dm_fread_le16(pack->file, &hdr.version) ||
        !dm_fread_le32(pack->file, &hdr.dirEntries) ||
        !dm_fread_le32(pack->file, &hdr.dirOffset))
    {
        dmPackClose(pack);
        return DMERR_FREAD;
    }

    // Check information
    if (memcmp(&hdr.ident, DPACK_IDENT, sizeof(hdr.ident)) != 0)
    {
        dmPackClose(pack);
        return DMERR_NOTPACK;
    }

    if (hdr.version != DPACK_VERSION)
    {
        dmPackClose(pack);
        return DMERR_VERSION;
    }

    // Read directory
    if (fseek(pack->file, hdr.dirOffset, SEEK_SET) != 0)
    {
        dmPackClose(pack);
        return DMERR_INVALID;
    }

    for (i = 0; i < hdr.dirEntries; i++)
    {
        // Allocate and read directory entry
        DMPackEntry *entry = dmPackEntryNew();

        if (entry == NULL)
        {
            dmPackClose(pack);
            return DMERR_MALLOC;
        }

        if (!dm_fread_str(pack->file, (Uint8 *) &entry->filename, sizeof(entry->filename)) ||
            !dm_fread_le32(pack->file, &entry->size) ||
            !dm_fread_le32(pack->file, &entry->offset) ||
            !dm_fread_le32(pack->file, &entry->length) ||
            !dm_fread_le32(pack->file, &entry->flags))
        {
            *ppPack = pack;
            return DMERR_FREAD;
        }

        // Insert into list
        dmPackEntryInsert(&pack->entries, entry);
    }

    // Set the result
    *ppPack = pack;
    return DMERR_OK;
}


/*
 * CLOSE the packfile
 */
int dmPackClose(DMPackFile * pack)
{
    DMPackEntry *node;

    if (pack == NULL)
        return DMERR_OK;

    // Write the directory
    node = pack->entries;
    while (node != NULL)
    {
        DMPackEntry *next = node->next;
        dmPackEntryFree(node);
        node = next;
    }

    // Close the file
    if (pack->file != NULL)
    {
        fclose(pack->file);
        pack->file = NULL;
    }

    // Free structures
    dmFree(pack->filename);
    pack->filename = NULL;

    // Free packfile
    pack->entries = NULL;
    dmFree(pack);

    return DMERR_OK;
}
