Mercurial > hg > dmlib
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