// dvxResource.c  -- DVX resource runtime API
//
// Reads the resource block appended to DXE3 files. The resource
// block is located by reading the footer at the end of the file.

#include "dvxResource.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


DvxResHandleT *dvxResOpen(const char *path) {
    if (!path) {
        return NULL;
    }

    FILE *f = fopen(path, "rb");

    if (!f) {
        return NULL;
    }

    // Read footer from end of file
    if (fseek(f, -(int32_t)sizeof(DvxResFooterT), SEEK_END) != 0) {
        fclose(f);
        return NULL;
    }

    DvxResFooterT footer;

    if (fread(&footer, sizeof(footer), 1, f) != 1) {
        fclose(f);
        return NULL;
    }

    if (footer.magic != DVX_RES_MAGIC || footer.entryCount == 0) {
        fclose(f);
        return NULL;
    }

    // Read directory
    if (fseek(f, footer.dirOffset, SEEK_SET) != 0) {
        fclose(f);
        return NULL;
    }

    DvxResDirEntryT *entries = (DvxResDirEntryT *)malloc(footer.entryCount * sizeof(DvxResDirEntryT));

    if (!entries) {
        fclose(f);
        return NULL;
    }

    if (fread(entries, sizeof(DvxResDirEntryT), footer.entryCount, f) != footer.entryCount) {
        free(entries);
        fclose(f);
        return NULL;
    }

    fclose(f);

    DvxResHandleT *h = (DvxResHandleT *)malloc(sizeof(DvxResHandleT));

    if (!h) {
        free(entries);
        return NULL;
    }

    strncpy(h->path, path, sizeof(h->path) - 1);
    h->path[sizeof(h->path) - 1] = '\0';
    h->entries    = entries;
    h->entryCount = footer.entryCount;

    return h;
}


const DvxResDirEntryT *dvxResFind(DvxResHandleT *h, const char *name) {
    if (!h || !name) {
        return NULL;
    }

    for (uint32_t i = 0; i < h->entryCount; i++) {
        if (strcmp(h->entries[i].name, name) == 0) {
            return &h->entries[i];
        }
    }

    return NULL;
}


void *dvxResRead(DvxResHandleT *h, const char *name, uint32_t *outSize) {
    const DvxResDirEntryT *entry = dvxResFind(h, name);

    if (!entry) {
        return NULL;
    }

    FILE *f = fopen(h->path, "rb");

    if (!f) {
        return NULL;
    }

    if (fseek(f, entry->offset, SEEK_SET) != 0) {
        fclose(f);
        return NULL;
    }

    void *buf = malloc(entry->size);

    if (!buf) {
        fclose(f);
        return NULL;
    }

    if (fread(buf, 1, entry->size, f) != entry->size) {
        free(buf);
        fclose(f);
        return NULL;
    }

    fclose(f);

    if (outSize) {
        *outSize = entry->size;
    }

    return buf;
}


void dvxResClose(DvxResHandleT *h) {
    if (h) {
        free(h->entries);
        free(h);
    }
}
