diff dmres.c @ 0:32250b436bca

Initial re-import.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 28 Sep 2012 01:54:23 +0300
parents
children e0fc7863d024
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmres.c	Fri Sep 28 01:54:23 2012 +0300
@@ -0,0 +1,951 @@
+/*
+ * dmlib
+ * -- Resource management
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2003-2012 Tecnic Software productions (TNSP)
+ */
+#include "dmres.h"
+#include <time.h>
+
+#if !defined(DMRES_PACKFS) && !defined(DMRES_STDIO)
+#error At least one of DMRES_PACKFS, DMRES_STDIO must be defined.
+#endif
+
+#define DMRES_LOCK(x) dmMutexLock(dfResourcesMutex)
+#define DMRES_UNLOCK(x) dmMutexUnlock(dfResourcesMutex)
+
+
+/* Global variables
+ */
+static BOOL        dfResInitialized = FALSE;
+static int         dfResFlags = 0;
+static char *      dfResPath = NULL;
+DMResource *       dfResources = NULL;
+DMMutex *          dfResourcesMutex = NULL;
+
+
+#ifdef DMRES_PACKFS
+static DMPackFile *dfResPackFile = NULL;
+static char *      dfResPackFilename = NULL;
+#endif
+
+
+DMResource *dmres_new(const char *filename, int flags, size_t size)
+{
+    DMResource *res = dmMalloc0(sizeof(DMResource));
+    if (res == NULL)
+        return NULL;
+    
+    res->filename = dm_strdup(filename);
+    res->flags = flags;
+    res->dataSize = size;
+    
+    return res;
+}
+
+
+void dmres_free(DMResource *res)
+{
+    if (res != NULL)
+    {
+        dmFree(res->filename);
+        dmFree(res->data);
+        dmFree(res);
+    }
+}
+
+
+void dmres_insert(DMResource * node)
+{
+    if (dfResources != NULL)
+    {
+        node->prev = dfResources->prev;
+        dfResources->prev->next = node;
+        dfResources->prev = node;
+    }
+    else
+    {
+        dfResources = node->prev = node;
+    }
+    
+    node->next = NULL;
+}
+
+
+void dmres_delete(DMResource * node)
+{
+    if (node->prev)
+        node->prev->next = node->next;
+
+    if (node->next)
+        node->next->prev = node->prev;
+    else
+        dfResources->prev = node->prev;
+
+    node->prev = node->next = NULL;
+}
+
+
+DMResource * dmres_find(const char *filename)
+{
+    DMResource *node, *found = NULL;
+
+    DMRES_LOCK();
+
+    for (node = dfResources; node != NULL; node = node->next)
+    {
+        if (strcmp(node->filename, filename) == 0)
+        {
+            found = node;
+            break;
+        }
+    }
+
+    DMRES_UNLOCK();
+
+    return found;
+}
+
+
+#ifdef DMRES_STDIO
+/* Basic stdio file routines
+ */
+static int dm_stdio_fopen(DMResource *handle)
+{
+    char *rfilename = dm_strdup_printf("%s%s", DMRES_DATA_PATH, handle->filename);
+    if (rfilename == NULL)
+        return DMERR_MALLOC;
+
+    handle->fh = fopen(rfilename, "rb");
+    dmFree(rfilename);
+
+    handle->error = dmGetErrno();
+    return (handle->fh != NULL) ? DMERR_OK : DMERR_FOPEN;
+}
+
+
+static void dm_stdio_fclose(DMResource * f)
+{
+    if (f->fh != NULL)
+    {
+        fclose(f->fh);
+        f->fh = NULL;
+    }
+}
+
+
+static int dm_stdio_ferror(DMResource * f)
+{
+    return f->error;
+}
+
+
+static int dm_stdio_fseek(DMResource *f, const off_t pos, const int whence)
+{
+    int ret = fseek(f->fh, pos, whence);
+    f->error = dmGetErrno();
+    return ret;
+}
+
+
+static off_t dm_stdio_fsize(DMResource *f)
+{
+    off_t savePos, fileSize;
+
+    // Check if the size is cached
+    if (f->dataSize != 0)
+        return f->dataSize;
+
+    // Get file size
+    savePos = ftell(f->fh);
+    if (fseek(f->fh, 0L, SEEK_END) != 0)
+    {
+        f->error = dmGetErrno();
+        return -1;
+    }
+
+    fileSize = ftell(f->fh);
+    if (fseek(f->fh, savePos, SEEK_SET) != 0)
+    {
+        f->error = dmGetErrno();
+        return -1;
+    }
+
+    f->dataSize = fileSize;
+    return fileSize;
+}
+
+
+static off_t dm_stdio_ftell(DMResource * f)
+{
+    return ftell(f->fh);
+}
+
+
+static BOOL dm_stdio_feof(DMResource * f)
+{
+    return feof(f->fh);
+}
+
+
+static int dm_stdio_fgetc(DMResource * f)
+{
+    int ret = fgetc(f->fh);
+    f->error = dmGetErrno();
+    return ret;
+}
+
+
+static size_t dm_stdio_fread(void *ptr, size_t size, size_t nmemb, DMResource * f)
+{
+    size_t ret = fread(ptr, size, nmemb, f->fh);
+    f->error = dmGetErrno();
+    return ret;
+}
+
+
+static int dm_stdio_preload(DMResource *handle)
+{
+    int ret = dm_stdio_fopen(handle);
+    if (ret != DMERR_OK)
+        return ret;
+    
+    dm_stdio_fsize(handle);
+    
+    handle->data = dmMalloc(handle->dataSize);
+    if (handle->data == NULL)
+        return DMERR_MALLOC;
+    
+    if (dm_stdio_fread(handle->data, sizeof(Uint8), handle->dataSize, handle) != handle->dataSize)
+        return DMERR_FREAD;
+    
+    return DMERR_OK;
+}
+
+
+DMResourceOps dfStdioFileOps =
+{
+    dm_stdio_ferror,
+    dm_stdio_fseek,
+    dm_stdio_fsize,
+    dm_stdio_ftell,
+    dm_stdio_feof,
+    dm_stdio_fgetc,
+    dm_stdio_fread,
+
+    dm_stdio_fopen,
+    dm_stdio_fclose,
+    dm_stdio_preload
+};
+
+DMResourceOps dfStdioFHOps =
+{
+    dm_stdio_ferror,
+    dm_stdio_fseek,
+    dm_stdio_fsize,
+    dm_stdio_ftell,
+    dm_stdio_feof,
+    dm_stdio_fgetc,
+    dm_stdio_fread,
+
+    NULL,
+    NULL,
+    NULL
+};
+#endif
+
+
+// Some mingw/windows headers define these as macros, which is bad for us
+#ifdef __WIN32
+#undef ferror
+#undef feof
+#endif
+
+
+/*
+ * PACK file routines
+ */
+#ifdef DMRES_PACKFS
+static int dm_pack_preload(DMResource *handle)
+{
+    DMPackEntry *node;
+    int res = DMERR_OK, cres, cdataLeft;
+    z_stream cstream;
+    Uint8 *  cbuffer = NULL;
+
+    // Search PACK nodelist for file
+    if ((node = dm_pack_find(dfResPackFile->entries, handle->filename)) == NULL)
+    {
+        dmError("Entry '%s' not found in PACK file.\n", handle->filename);
+        res = DMERR_NOT_FOUND;
+        goto error;
+    }
+
+    // Seek to entry
+    if (fseek(dfResPackFile->file, node->offset, SEEK_SET) == -1)
+    {
+        dmError("Could not seek node position in PACK file.\n");
+        res = DMERR_FSEEK;
+        goto error;
+    }
+
+    // Allocate a structures and buffers
+    cbuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE);
+    if (cbuffer == NULL)
+    {
+        res = DMERR_MALLOC;
+        goto error;
+    }
+
+    // Initialize fields
+    handle->dataOffset = 0;
+    handle->dataSize = node->size;
+    handle->data = (Uint8 *) dmMalloc(node->size);
+    if (handle->data == NULL)
+    {
+        res = DMERR_MALLOC;
+        goto error;
+    }
+
+    // Initialize decompression
+    cstream.zalloc = (alloc_func) Z_NULL;
+    cstream.zfree = (free_func) Z_NULL;
+    cstream.opaque = (voidpf) Z_NULL;
+    cstream.next_out = handle->data;
+    cstream.avail_out = handle->dataSize;
+    cdataLeft = node->length;
+    cres = inflateInit(&(cstream));
+    if (cres != Z_OK)
+    {
+        dmError("Could not initialize zlib stream inflation.\n");
+        res = DMERR_INIT_FAIL;
+        goto error;
+    }
+
+    // Uncompress the data
+    while (cdataLeft > 0 &&
+           cstream.avail_out > 0 && cres == Z_OK)
+    {
+        cstream.avail_in = fread(
+            cbuffer, sizeof(Uint8),
+            (cdataLeft >= DPACK_TMPSIZE) ? DPACK_TMPSIZE : cdataLeft,
+            dfResPackFile->file);
+
+        cdataLeft -= cstream.avail_in;
+        cstream.next_in = cbuffer;
+        cres = inflate(&cstream, Z_FULL_FLUSH);
+    }
+
+    // Cleanup
+    inflateEnd(&(cstream));
+
+error:
+    dmFree(cbuffer);
+    return res;
+}
+#endif
+
+
+static void dm_mem_fclose(DMResource * f)
+{
+    f->dataSize = 0;
+    f->dataOffset = 0;
+    dmFree(f->data);
+    f->data = NULL;
+}
+
+
+static int dm_mem_ferror(DMResource * f)
+{
+    return f->error;
+}
+
+
+static int dm_mem_fseek(DMResource * f, const off_t offset, const int whence)
+{
+    off_t newPos;
+
+    // Calculate the new position
+    switch (whence)
+    {
+        case SEEK_SET:
+            newPos = offset;
+            break;
+
+        case SEEK_CUR:
+            newPos = f->dataOffset + offset;
+            break;
+
+        case SEEK_END:
+            newPos = f->dataSize + offset;
+            break;
+
+        default:
+            return -1;
+    }
+
+    // Set the new position
+    f->dataOffset = newPos;
+
+    // Check the new position
+    if (newPos < 0 && (size_t) newPos >= f->dataSize)
+        return -1;
+
+    return 0;
+}
+
+
+static off_t dm_mem_fsize(DMResource * f)
+{
+    return f->dataSize;
+}
+
+
+static off_t dm_mem_ftell(DMResource * f)
+{
+    return f->dataOffset;
+}
+
+
+static BOOL dm_mem_feof(DMResource * f)
+{
+    // Check for EOF
+    if ((size_t) f->dataOffset <= f->dataSize)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+
+static int dm_mem_fgetc(DMResource * f)
+{
+    // Check for EOF
+    if ((size_t) f->dataOffset < f->dataSize)
+        return (int) f->data[f->dataOffset++];
+    else
+        return EOF;
+}
+
+
+static size_t dm_mem_fread(void *buf, size_t size, size_t nmemb, DMResource * f)
+{
+    size_t length = (size * nmemb);
+
+    // Check if we can read the whole chunk
+    if (((size_t) f->dataOffset + length) >= f->dataSize)
+    {
+        nmemb = (f->dataSize - f->dataOffset) / size;
+        length = size * nmemb;
+    }
+
+    memcpy(buf, f->data + f->dataOffset, length);
+    f->dataOffset += length;
+    return nmemb;
+}
+
+
+DMResourceOps dfPackFileOps =
+{
+    dm_mem_ferror,
+    dm_mem_fseek,
+    dm_mem_fsize,
+    dm_mem_ftell,
+    dm_mem_feof,
+    dm_mem_fgetc,
+    dm_mem_fread,
+    
+    NULL,
+    dm_mem_fclose,
+    dm_pack_preload
+};
+
+
+DMResourceOps dfMemIOFileOps =
+{
+    dm_mem_ferror,
+    dm_mem_fseek,
+    dm_mem_fsize,
+    dm_mem_ftell,
+    dm_mem_feof,
+    dm_mem_fgetc,
+    dm_mem_fread,
+    
+    NULL,
+    NULL,
+    NULL
+};
+
+
+/* FS file handling functions. These functions call the actual
+ * functions depending on where the file is located.
+ */
+DMResource *dmf_open(const char *filename)
+{
+    int ret;
+    DMResource *handle;
+
+    // Check master directory for resource
+    if ((handle = dmres_find(filename)) == NULL)
+    {
+#ifdef DMRES_STDIO
+        // Hmm.. does not exist? Fall back to a stdio file
+        handle = dmres_new(filename, 0, 0);
+        if (handle == NULL)
+            return NULL;
+
+        handle->fops = &dfStdioFileOps;
+        dmres_insert(handle);
+#else
+        // Stdio not enabled, fail
+        return NULL;
+#endif
+    }
+
+    // Check fops
+    if (handle->fops == NULL)
+    {
+#ifdef DMRES_PACKFS
+        if (dfResFlags & DRF_USE_PACK)
+            handle->fops = &dfPackFileOps;
+#ifdef DMRES_STDIO
+        else
+            handle->fops = &dfStdioFileOps;
+#else
+        handle->fops = &dfPackFileOps;
+#endif
+
+#else
+        handle->fops = &dfStdioFileOps;
+#endif
+    }
+
+    // Check if the data is preloaded
+    if (handle->flags & DMF_LOADED)
+    {
+        dmres_ref(handle);
+        return handle;
+    }
+
+    // Check if we want to preload ..
+    ret = DMERR_INIT_FAIL;
+    if ((handle->flags & DMF_PRELOAD) &&
+        handle->fops->preload != NULL)
+        ret = handle->fops->preload(handle);
+    else
+    {
+        if (handle->fops->fopen != NULL)
+            ret = handle->fops->fopen(handle);
+        else
+        if (handle->fops->preload != NULL)
+            ret = handle->fops->preload(handle);
+    }
+    
+    if (ret == DMERR_OK)
+        return handle;
+
+    dmres_ref(handle);
+    return NULL;
+}
+
+
+DMResource * dmf_open_memio(const char *filename, Uint8 *buf, size_t len)
+{
+    DMResource *handle;
+
+    // Check master directory for resource
+    if ((handle = dmres_find(filename)) == NULL)
+    {
+        // Hmm.. does not exist? Fall back to a stdio file
+        handle = dmres_new(filename, DMF_LOADED, len);
+        if (handle == NULL)
+            return NULL;
+
+        handle->fops = &dfMemIOFileOps;
+        handle->data = buf;
+        dmres_insert(handle);
+    }
+
+    // Increase refcount
+    dmres_ref(handle);
+
+    return handle;
+}
+
+
+#ifdef DMRES_STDIO
+DMResource * dmf_create_stdio(const char *filename)
+{
+    DMResource *handle = dmres_new(filename, 0, 0);
+    if (handle == NULL)
+        return NULL;
+
+    handle->fops = &dfStdioFileOps;
+
+    handle->fh = fopen(filename, "rb");
+    handle->error = dmGetErrno();
+    
+    if (handle->fh != NULL)
+    {
+        dmres_ref(handle);
+        return handle;
+    }
+    else
+    {
+        dmres_free(handle);
+        return NULL;
+    }
+}
+
+
+DMResource * dmf_create_stdio_stream(FILE *fh)
+{
+    DMResource *handle = dmres_new("", 0, 0);
+    if (handle == NULL)
+        return NULL;
+
+    handle->fops = &dfStdioFHOps;
+    handle->fh = fh;
+    dmres_ref(handle);
+    return handle;
+}
+#endif
+
+
+void dmf_close(DMResource * f)
+{
+    if (f == NULL)
+        return;
+
+    if (f->fops->fclose != NULL)
+        f->fops->fclose(f);
+
+    dmres_unref(f);
+}
+
+
+int dmferror(DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->ferror(f);
+}
+
+int dmfseek(DMResource * f, off_t offset, int whence)
+{
+    f->atime = time(NULL);
+    return f->fops->fseek(f, offset, whence);
+}
+
+off_t dmfsize(DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->fsize(f);
+}
+
+off_t dmftell(DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->ftell(f);
+}
+
+BOOL dmfeof(DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->feof(f);
+}
+
+int dmfgetc(DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->fgetc(f);
+}
+
+size_t dmfread(void *ptr, size_t size, size_t nmemb, DMResource * f)
+{
+    f->atime = time(NULL);
+    return f->fops->fread(ptr, size, nmemb, f);
+}
+
+
+int dmres_ref(DMResource *res)
+{
+    DMRES_LOCK();
+    res->atime = time(NULL);
+    res->refcount++;
+    DMRES_UNLOCK();
+
+    return res->refcount;
+}
+
+
+int dmres_unref(DMResource *res)
+{
+    DMRES_LOCK();
+    res->refcount--;
+    DMRES_UNLOCK();
+
+    return res->refcount;
+}
+
+
+int dmres_load_resfile(const char *filename)
+{
+    int ret;
+    char line[256];
+    FILE *f = fopen(filename, "r");
+    if (f == NULL)
+        return DMERR_FOPEN;
+
+    DMRES_LOCK();
+
+    while (fgets(line, sizeof(line) - 1, f) != NULL)
+    {
+        int fnstart, fsep;
+        for (fnstart = 0; isspace(line[fnstart]); fnstart++);
+        for (fsep = fnstart; line[fsep] && line[fsep] != '|'; fsep++);
+        if (line[fsep] == '|')
+        {
+            int flags, i;
+            for (i = fsep - 1; i > 0 && isspace(line[i]); i--)
+                line[i] = 0;
+
+            for (i = fsep; isspace(line[i]); i++);
+            
+            if (sscanf(&line[i], "%x", &flags) == 1 &&
+                strlen(&line[fnstart]) > 0)
+            {
+                
+            }
+        }
+    }
+
+    DMRES_UNLOCK();
+    fclose(f);
+    return ret;
+}
+
+
+int dmres_write_resfile(const char *filename)
+{
+    int ret;
+    DMResource *node;
+    FILE *f = fopen(filename, "w");
+    if (f == NULL)
+        return DMERR_FOPEN;
+
+    DMRES_LOCK();
+    
+    for (node = dfResources; node != NULL; node = node->next)
+    {
+        if (fprintf(f, "%s|%08x\n", node->filename, node->flags) < 0)
+        {
+            ret = DMERR_FWRITE;
+            goto error;
+        }
+    }
+
+error:
+    DMRES_UNLOCK();
+    fclose(f);
+    return ret;
+}
+
+
+/* Resources subsystem initialization and shutdown routines
+ */
+int dmres_init(const char *filename, const char *path, int flags)
+{
+    // Check if we are already initialized
+    if (dfResInitialized)
+        return DMERR_ALREADY_INIT;
+
+    dfResFlags        = flags; 
+    dfResPath         = dm_strdup((path != NULL) ? path : DMRES_DATA_PATH);
+    dfResourcesMutex  = dmCreateMutex();
+
+    if (flags & DRF_USE_PACK)
+    {
+#ifdef DMRES_PACKFS
+        int ret;
+        DMPackEntry *node;
+
+        dfResPackFilename = dm_strdup((filename != NULL) ? filename : DMRES_DATA_PACK);
+
+        // Initialize PACK, open as read-only
+        ret = dm_pack_open(dfResPackFilename, &dfResPackFile, TRUE);
+        if (ret != DMERR_OK)
+        {
+            dmError("Error opening PACK file '%s', #%i: %s\n",
+                dfResPackFilename, ret, dmErrorStr(ret));
+
+            return DMERR_INIT_FAIL;
+        }
+        
+        // Initialize resources from a PACK file
+        for (node = dfResPackFile->entries; node != NULL; node = node->next)
+        {
+            DMResource *res = dmres_new(node->filename, node->resFlags, node->size);
+            if (res == NULL)
+            {
+                dmError("Could not allocate memory for resource node '%s' [0x%08x], %d.\n",
+                    node->filename, node->resFlags, node->size);
+                return DMERR_INIT_FAIL;
+            }
+
+            dmres_insert(res);
+        }
+        
+#else
+        // PACK not compiled in, FAIL!
+        return DMERR_INIT_FAIL;
+#endif
+    }
+    else
+    {
+        // Initialize resources from a resource directory
+        char *resFilename = dm_strdup_printf("%s%s", dfResPath, DMRES_RES_FILE);
+        int ret = dmres_load_resfile(resFilename);
+        dmFree(resFilename);
+        
+        if (ret != DMERR_OK)
+            return DMERR_INIT_FAIL;
+    }
+
+
+    // Initialization complete
+    dfResInitialized = TRUE;
+    return DMERR_OK;
+}
+
+
+void dmres_close(void)
+{
+    DMResource *node;
+    DMRES_LOCK();
+
+    if (!dfResInitialized)
+        return;
+    
+    // Shutdown possible subsystems
+#ifdef DMRES_PACKFS
+    if (dfResFlags & DRF_USE_PACK)
+    {
+        int res = dm_pack_close(dfResPackFile);
+        if (res != DMERR_OK)
+        {
+            dmError("Error closing PACK, #%i: %s\n",
+                        res, dmErrorStr(res));
+        }
+
+        dmFree(dfResPackFilename);
+    }
+#endif
+
+    // Free resource entries
+    node = dfResources;
+    while (node != NULL)
+    {
+        DMResource *next = node->next;
+        dmres_free(node);
+        node = next;
+    }
+
+    // Etc.
+    dmFree(dfResPath);
+    DMRES_UNLOCK();
+    dmDestroyMutex(dfResourcesMutex);
+    dfResInitialized = FALSE;
+}
+
+
+BOOL dmres_preload(int *loaded, int *total)
+{
+    static DMResource *dfPreload = NULL;
+
+    DMRES_LOCK();
+    
+    if (dfPreload == NULL)
+    {
+        DMResource *node;
+        dfPreload = dfResources;
+        *loaded = 0;
+        *total = 0;
+        for (node = dfResources; node != NULL; node = node->next)
+        {
+            if (node->flags & DMF_PRELOAD)
+                (*total)++;
+        }
+    }
+
+    if (dfPreload != NULL)
+    {
+        if (dfPreload->flags & DMF_PRELOAD)
+        {
+            (*loaded)++;
+        }
+
+        dfPreload = dfPreload->next;
+    }
+
+    DMRES_UNLOCK();
+    
+    return (*total) == (*loaded);
+}
+
+
+void dmres_prune(int agems, int flags)
+{
+    DMResource *node, *next;
+    int currtime = time(NULL);
+    DMRES_LOCK();
+
+    node = dfResources;
+    while (node != NULL)
+    {
+        next = node->next;
+        // Check if node has refcount of 0 and is
+        // not marked as persistent resource
+        if (node->refcount == 0 && (node->flags & DMF_PERSIST) == 0)
+        {
+            // Check if we match either one of atime or mtime
+            if (((flags & DMPRUNE_ATIME) &&
+                currtime - node->atime >= agems) ||
+                ((flags & DMPRUNE_MTIME) &&
+                currtime - node->mtime >= agems))
+            {
+                dmres_delete(node);
+            }
+        }
+        node = next;
+    }
+
+    DMRES_UNLOCK();
+}
+
+
+/* Helper resource access routines
+ */
+int dmf_read_str(DMResource *f, Uint8 *s, size_t l)
+{
+    return dmfread(s, sizeof(Uint8), l, f) == l;
+}
+
+
+#define DM_DEFINE_FUNC(xname, xtype, xmacro)          \
+BOOL dmf_read_ ## xname (DMResource *f, xtype *v) {      \
+    xtype result;                                     \
+    if (dmfread(&result, sizeof( xtype ), 1, f) != 1) \
+        return FALSE;                                 \
+    *v = DM_ ## xmacro ## _TO_NATIVE (result);        \
+    return TRUE;                                      \
+}
+
+DM_DEFINE_FUNC(le16, Uint16, LE16)
+DM_DEFINE_FUNC(le32, Uint32, LE32)
+
+DM_DEFINE_FUNC(be16, Uint16, BE16)
+DM_DEFINE_FUNC(be32, Uint32, BE32)
+
+#ifdef DM_HAVE_64BIT
+DM_DEFINE_FUNC(le64, Uint64, LE64)
+DM_DEFINE_FUNC(be64, Uint64, BE64)
+#endif