view src/dmpack.c @ 2479:c1cae47cd410

Rename DMPackEntry::length to csize (compressed size).
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 27 Apr 2020 21:33:29 +0300
parents 69a5af2eb1ea
children 9807ae37ad69
line wrap: on
line source

/*
 * DMLib
 * -- PACK-file handling
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2011-2015 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 = NULL;
    DMPackFileHeader hdr;
    int ret = DMERR_OK;

    // Allocate packfile-structure
    if ((pack = dmMalloc0(sizeof(DMPackFile))) == NULL)
    {
        ret = dmErrorDBG(DMERR_MALLOC,
            "Failed to allocate memory for PACK file structure '%s'.\n",
            filename);
        goto out;
    }

    // Open the file
    if ((pack->file = fopen(filename, readOnly ? "rb" : "r+b")) == NULL)
    {
        ret = dmError(dmGetErrno(),
            "Failed to open PACK file '%s'.\n",
            filename);
        goto out;
    }

    pack->filename = dm_strdup(filename);

    // Read PACK header
    if (fseeko(pack->file, 0L, SEEK_SET) != 0)
    {
        ret = dmErrorDBG(dmGetErrno(),
            "Failed to seek to file start '%s'.\n",
            filename);
        goto out;
    }

    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_le64(pack->file, &hdr.dirOffset))
    {
        ret = dmErrorDBG(DMERR_FREAD,
            "Error reading PACK file header data.\n");
        goto out;
    }

    // Check information
    if (memcmp(&hdr.ident, DPACK_IDENT, sizeof(hdr.ident)) != 0)
    {
        ret = DMERR_NOTPACK;
        goto out;
    }

    if (hdr.version != DPACK_VERSION)
    {
        ret = DMERR_PACK_VERSION;
        goto out;
    }

    // Read directory
    if (hdr.dirOffset < sizeof(hdr) ||
        fseeko(pack->file, hdr.dirOffset, SEEK_SET) != 0)
    {
        ret = dmErrorDBG(DMERR_INVALID,
            "Error seeking to PACK file '%s' directory offset.\n",
            filename);
        goto out;
    }

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

        if ((entry = dmPackEntryNew()) == NULL)
        {
            ret = DMERR_MALLOC;
            goto out;
        }

        if (!dm_fread_str(pack->file, (Uint8 *) &entry->filename, DMRES_NAME_LEN) ||
            !dm_fread_le64(pack->file, &entry->offset) ||
            !dm_fread_le32(pack->file, &entry->csize) ||
            !dm_fread_le32(pack->file, &entry->size) ||
            !dm_fread_le32(pack->file, &entry->flags))
        {
            ret = DMERR_FREAD;
            goto out;
        }

        // Ensure that the filename ends in NUL
        entry->filename[DMRES_NAME_LEN] = 0;

        // Validate
        if (entry->size == 0 || entry->csize == 0 ||
            entry->csize > hdr.dirOffset ||
            entry->offset > hdr.dirOffset ||
            strlen(entry->filename) == 0)
        {
            ret = dmErrorDBG(DMERR_INVALID,
                "Invalid PACK entry #%d '%s'\n", i,
                entry->filename);
            goto out;
        }

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

out:
    if (ret != DMERR_OK)
    {
        dmPackClose(pack);
        pack = NULL;
    }

    *ppPack = pack;

    return ret;
}


/*
 * 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);

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

    // Free packfile
    memset(pack, 0, sizeof(DMPackFile));
    dmFree(pack);

    return DMERR_OK;
}