view dmres.c @ 96:6bf5220fa47e

Urgh .. use memset to silence some bogus GCC warnings about using potentially uninitialized values, while that will not actually be possible. In any case, it is annoying.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 02 Oct 2012 18:52:28 +0300
parents 23ac82365a65
children 1c2ff205fa0e
line wrap: on
line source

/*
 * 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 *node = dmMalloc0(sizeof(DMResource));
    if (node == NULL)
        return NULL;
    
    node->filename = dm_strdup(filename);
    node->flags = flags;
    node->dataSize = size;
    
    return node;
}


void dmres_free_res_data(DMResource *node)
{
    if (node->rdata != NULL &&
        node->rops != NULL &&
        node->rops->free != NULL)
    {
        node->rops->free(node);
    }

    node->rdata = NULL;
    node->flags &= !DMF_LOADED_RES;
}


void dmres_free_raw_data(DMResource *node)
{
    dmFree(node->data);
    node->data = NULL;
    node->flags &= !DMF_LOADED_RAW;
}


void dmres_purge_raw_data(DMResource *node)
{
    if ((node->flags & DMF_PRELOAD_RAW) == 0 &&
        (node->flags & DMF_LOADED_RAW) &&
        node->data != NULL)
        dmres_free_raw_data(node);
}


void dmres_free(DMResource *node)
{
    if (node != NULL)
    {
        dmres_free_res_data(node);
        dmres_free_raw_data(node);
        dmFree(node->filename);
        dmFree(node);
    }
}


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 int dm_stdio_fputc(int v, DMResource * f)
{
    int ret = fputc(v, 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 size_t dm_stdio_fwrite(void *ptr, size_t size, size_t nmemb, DMResource * f)
{
    size_t ret = fwrite(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_fputc,
    dm_stdio_fread,
    dm_stdio_fwrite,

    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_fputc,
    dm_stdio_fread,
    dm_stdio_fwrite,

    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,
    NULL,
    dm_mem_fread,
    NULL,
    
    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,
    NULL,
    dm_mem_fread,
    NULL,
    
    NULL,
    NULL,
    NULL
};


/* FS file handling functions. These functions call the actual
 * functions depending on where the file is located.
 */
static void dmf_init_fops(DMResource *handle)
{
    // 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
    }
}


int dmf_preload(DMResource *handle)
{
    int ret = DMERR_INIT_FAIL;

    // Check if we want to preload raw data?
    if (((handle->flags & DMF_PRELOAD_RAW) ||
        (dfResFlags & DRF_PRELOAD_ALL)) &&
        (handle->flags & DMF_LOADED_RAW) == 0 &&
        handle->fops->preload != NULL)
    {
        ret = handle->fops->preload(handle);
        if (ret == DMERR_OK)
        {
            handle->flags |= DMF_LOADED_RAW;
        }
    }
    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)
            {
                handle->flags |= DMF_LOADED_RAW;
            }
        }
    }

    // Check if resource data is to be preloaded
    if (((handle->flags & DMF_PRELOAD_RES) || (dfResFlags & DRF_PRELOAD_RES)) &&
        (handle->flags & DMF_LOADED_RES) == 0 &&
        handle->rops != NULL &&
        handle->rops->load != NULL)
    {
        ret = handle->rops->load(handle);
        if (ret == DMERR_OK)
        {
            // Okay, mark as loaded
            handle->flags |= DMF_LOADED_RES;

            // Check if we can purge the raw data now
            if ((handle->flags & DMF_PERSIST) == 0)
                dmres_purge_raw_data(handle);
        }
    }

    return ret;
}


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
    }

    // Initialize file ops
    dmf_init_fops(handle);

    // Check if the data is preloaded
    if (handle->flags & DMF_LOADED_RAW)
    {
        dmres_ref(handle);
        return handle;
    }

    // Try preloading
    ret = dmf_preload(handle);
    
    if (ret == DMERR_OK)
    {
        dmres_ref(handle);
        return 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_RAW, 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, const char *mode)
{
    DMResource *handle = dmres_new(filename, 0, 0);
    if (handle == NULL)
        return NULL;

    handle->fops = &dfStdioFileOps;

    handle->fh = fopen(filename, mode);
    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);
}

int dmfputc(int v, DMResource * f)
{
    f->atime = time(NULL);
    return f->fops->fputc(v, 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);
}

size_t dmfwrite(void *ptr, size_t size, size_t nmemb, DMResource * f)
{
    f->atime = time(NULL);
    return f->fops->fwrite(ptr, size, nmemb, f);
}


int dmres_ref(DMResource *node)
{
    DMRES_LOCK();
    node->atime = time(NULL);
    node->refcount++;
    DMRES_UNLOCK();

    return node->refcount;
}


int dmres_unref(DMResource *node)
{
    DMRES_LOCK();
    node->refcount--;
    DMRES_UNLOCK();

    return node->refcount;
}


int dmres_load_resfile(const char *filename)
{
    int ret = DMERR_OK;
    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, int (*classifier)(DMResource *))
{
    // 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 & DMF_MASK, 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;
    }

    // Okay, classify resources
    if (dfResources != NULL && classifier != NULL)
    {
        DMResource *node;
        for (node = dfResources; node != NULL; node = node->next)
        {
            int ret = classifier(node);
            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;
}


int dmres_preload(BOOL start, int *loaded, int *total)
{
    static DMResource *dfPreload = NULL;
    int ret = DMERR_OK;

    DMRES_LOCK();
    
    // Initialize preloading 
    if (dfPreload == NULL || start)
    {
        DMResource *node;
        
        dfPreload = dfResources;
        *loaded = 0;
        *total = 0;

        // Calculate total number of resources to be preloaded
        for (node = dfResources; node != NULL; node = node->next)
        {
            if ((dfResFlags & (DRF_PRELOAD_ALL | DRF_PRELOAD_RES)) ||
                (node->flags & (DMF_PRELOAD_RAW | DMF_PRELOAD_RES)))
                (*total)++;
        }
    }
    else
    if (dfPreload != NULL)
    {
        // Initialize fops and preload
        dmf_init_fops(dfPreload);
        if ((ret = dmf_preload(dfPreload)) != DMERR_OK)
            goto error;

        (*loaded)++;
        dfPreload = dfPreload->next;
    }

    DMRES_UNLOCK();
    return (dfPreload == NULL) ? DMERR_OK : DMERR_PROGRESS;

error:
    DMRES_UNLOCK();
    return ret;
}


void dmres_prune(int agems, int flags)
{
    DMResource *node;
    int currtime = time(NULL);
    DMRES_LOCK();

    for (node = dfResources; node != NULL; node = 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 &&
            (node->flags & (DMF_LOADED_RES | DMF_LOADED_RAW)))
        {
            // 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_free_res_data(node);
                dmres_free_raw_data(node);
            }
        }
    }

    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