Mercurial > hg > dmlib
view src/dmres.c @ 1294:9f2117f1584a
Improve error checking in PCX writer.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 18 Aug 2017 21:35:09 +0300 |
parents | 086f49d616ac |
children | 93d1050eac99 |
line wrap: on
line source
/* * dmlib * -- Resource management * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2003-2015 Tecnic Software productions (TNSP) */ #include "dmres.h" #include <time.h> #ifdef DM_USE_PACKFS # ifdef DM_USE_ZLIB # include <zlib.h> # else # include "dmzlib.h" # endif #endif #ifdef DM_USE_STDIO # include <sys/types.h> # include <sys/stat.h> # include <unistd.h> # include <dirent.h> #endif DMResource *dmResourceNew(DMResourceLib *lib, const char *filename, const size_t size, const int flags) { DMResource *node = dmMalloc0(sizeof(DMResource)); if (node == NULL) return NULL; node->lib = lib; node->filename = dm_strdup(filename); node->rawSize = size; node->flags = flags; return node; } void dmResourceFreeResData(DMResource *node) { if (node->resData != NULL) { if (node->rops != NULL && node->rops->free != NULL) node->rops->free(node); else dmFree(node->resData); node->resData = NULL; } node->flags &= ~DMF_LOADED_RES; } void dmResourceFreeRawData(DMResource *node) { if ((node->flags & DMF_UNALLOCATED) == 0) { dmFree(node->rawData); node->rawData = NULL; node->flags &= ~DMF_LOADED_RAW; } } void dmResourceFree(DMResource *node) { if (node != NULL) { #ifdef DM_DEBUG if (node->lib != NULL) dmMutexLock(node->lib->mutex); if (node->refcount > 0) { dmErrorMsg( "Attempting to dmResourceFree(%p) node that has resfs %d > 0: '%s'.\n" "FOPS=%p, fops->name=%s\n", node, node->refcount, node->filename, node->fops, (node->fops != NULL) ? node->fops->name : "UNDEF"); } if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); #endif dmResourceFreeResData(node); dmResourceFreeRawData(node); dmFree(node->filename); dmFree(node); } } void dmResourceInsert(DMResourceLib *lib, DMResource * node) { if (lib == NULL || node == NULL) return; node->lib = lib; if (lib->resources != NULL) { node->prev = lib->resources->prev; lib->resources->prev->next = node; lib->resources->prev = node; } else { lib->resources = node->prev = node; } node->next = NULL; } void dmResourceDelete(DMResourceLib *lib, DMResource * node) { if (lib == NULL) return; if (node->prev) node->prev->next = node->next; if (node->next) node->next->prev = node->prev; else lib->resources->prev = node->prev; node->prev = node->next = NULL; } DMResource * dmResourceFind(DMResourceLib *lib, const char *filename) { DMResource *node, *found = NULL; if (lib == NULL) return NULL; dmMutexLock(lib->mutex); for (node = lib->resources; node != NULL; node = node->next) { if (strcmp(node->filename, filename) == 0) { found = node; break; } } dmMutexUnlock(lib->mutex); return found; } #ifdef DM_USE_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 off_t dm_stdio_ftell(DMResource * f) { return ftello(f->fh); } static int dm_stdio_fseek(DMResource *f, const off_t pos, const int whence) { int ret = fseeko(f->fh, pos, whence); f->error = dmGetErrno(); return ret; } static int dm_stdio_freset(DMResource * f) { if (f->fh != NULL) return dm_stdio_fseek(f, 0, SEEK_SET); else return DMERR_OK; } static off_t dm_stdio_fsize(DMResource *f) { off_t savePos, fileSize; // Check if the size is cached if (f->rawSize != 0) return f->rawSize; // Get file size if ((savePos = dm_stdio_ftell(f)) < 0) return -1; if (dm_stdio_fseek(f, 0, SEEK_END) != 0) return -1; if ((fileSize = dm_stdio_ftell(f)) < 0) return -1; if (dm_stdio_fseek(f, savePos, SEEK_SET) != 0) return -1; f->rawSize = fileSize; return fileSize; } 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(const 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->rawData = dmMalloc(handle->rawSize); if (handle->rawData == NULL) return DMERR_MALLOC; if (dm_stdio_fread(handle->rawData, sizeof(Uint8), handle->rawSize, handle) != handle->rawSize) return DMERR_FREAD; return DMERR_OK; } DMResourceOps dfStdioFileOps = { "Stdio", dm_stdio_freset, 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 = { "StdioFH", dm_stdio_freset, 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 DM_WINDOWS # undef ferror # undef feof #endif /* * PACK file routines */ #ifdef DM_USE_PACKFS #ifdef DM_USE_ZLIB #define DMRES_TMPBUF_SIZE (128 * 1024) static int dm_pack_decompress(DMResource *handle, DMPackEntry *node) { int ret = DMERR_OK, zret, cdataLeft; Uint8 * cbuffer = NULL; z_stream zstr; BOOL zinit = FALSE; // Allocate a structures and buffers if ((cbuffer = dmMalloc(DMRES_TMPBUF_SIZE)) == NULL) { ret = DMERR_MALLOC; goto out; } // Initialize decompression dmMemset(&zstr, 0, sizeof(zstr)); zstr.next_out = handle->rawData; zstr.avail_out = handle->rawSize; cdataLeft = node->length; if ((zret = inflateInit(&zstr)) != Z_OK) { ret = dmErrorDBG(DMERR_INIT_FAIL, "Could not initialize zlib stream decompression.\n"); goto out; } zinit = TRUE; // Uncompress the data while (cdataLeft > 0 && zstr.avail_out > 0 && zret == Z_OK) { zstr.avail_in = fread( cbuffer, sizeof(Uint8), (cdataLeft >= DMRES_TMPBUF_SIZE) ? DMRES_TMPBUF_SIZE : cdataLeft, handle->lib->packFile->file); cdataLeft -= zstr.avail_in; zstr.next_in = cbuffer; zret = inflate(&zstr, Z_FULL_FLUSH); } out: // Cleanup if (zinit) inflateEnd(&zstr); dmFree(cbuffer); return ret; } #else static int dm_pack_decompress(DMResource *handle, DMPackEntry *node) { DMZLibContext ctx; Uint8 *inBuf = NULL; int ret; // Allocate buffer for compressed data if ((inBuf = dmMalloc(node->length)) == NULL) { ret = DMERR_MALLOC; goto out; } // Read compressed data if (fread(inBuf, sizeof(Uint8), node->length, handle->lib->packFile->file) != node->length) { ret = DMERR_FREAD; goto out; } // Initialize decompression structures if ((ret = dmZLibInitInflate(&ctx)) != DMERR_OK) goto out; ctx.inBuffer = ctx.inBufferStart = inBuf; ctx.inBufferEnd = inBuf + node->length; ctx.outBuffer = ctx.outBufferStart = handle->rawData; ctx.outBufferEnd = handle->rawData + node->size; ctx.expandable = FALSE; // Attempt decompression if ((ret = dmZLibParseHeader(&ctx, TRUE)) != DMERR_OK) goto out; if ((ret = dmZLibInflate(&ctx)) != DMERR_OK) goto out; handle->rawData = ctx.outBufferStart; handle->rawSize = ctx.outBuffer - ctx.outBufferStart; out: dmZLibCloseInflate(&ctx); dmFree(inBuf); return ret; } #endif static int dm_pack_preload(DMResource *handle) { DMPackEntry *node; int ret = DMERR_OK; if (handle->lib == NULL || handle->lib->packFile == NULL) return DMERR_NULLPTR; // Search PACK nodelist for file if ((node = dmPackFind(handle->lib->packFile->entries, handle->filename)) == NULL) { ret = dmErrorDBG(DMERR_NOT_FOUND, "Entry '%s' not found in PACK file.\n", handle->filename); goto out; } // Seek to entry if (fseeko(handle->lib->packFile->file, node->offset, SEEK_SET) != 0) { ret = dmErrorDBG(DMERR_FSEEK, "Could not seek node position in PACK file.\n"); goto out; } // Allocate memory for the node if ((handle->rawData = dmMalloc(node->size)) == NULL) { ret = dmErrorDBG(DMERR_MALLOC, "Failed to allocate node data for '%s' (%d bytes).\n", handle->filename, node->size); goto out; } // Check if the entry is compressed if (handle->flags & DMF_COMPRESSED) { if ((ret = dm_pack_decompress(handle, node)) != DMERR_OK) goto out; if (handle->rawSize != node->size) { ret = dmErrorDBG(DMERR_COMPRESSION, "Decompressed data size for '%s' does not match size stored in PACK entry (%d <> %d).\n", handle->filename, handle->rawSize, node->size); } } else { if (node->size != node->length) { ret = dmErrorDBG(DMERR_INVALID_DATA, "Node '%s' raw size and length fields differ for uncompressed node: %d <> %d.\n", handle->filename, node->size, node->length); goto out; } if (fread(handle->rawData, sizeof(Uint8), node->size, handle->lib->packFile->file) != node->size) { ret = dmErrorDBG(DMERR_FREAD, "Error reading raw node data '%s', %d bytes.\n", handle->filename, node->size); goto out; } } out: return ret; } static int dm_pack_fopen(DMResource * f) { if ((f->flags & DMF_LOADED_RAW) == 0) { int ret = dm_pack_preload(f); if (ret == DMERR_OK) f->flags |= DMF_LOADED_RAW; return ret; } else return DMERR_OK; } static void dm_pack_fclose(DMResource * f) { if ((f->flags & DMF_PERSIST) == 0) dmResourceFreeRawData(f); } #endif static int dm_mem_freset(DMResource * f) { f->rawOffset = 0; return DMERR_OK; } 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->rawOffset + offset; break; case SEEK_END: newPos = f->rawSize + offset; break; default: return -1; } // Set the new position f->rawOffset = newPos; // Check the new position if (newPos < 0 && (size_t) newPos >= f->rawSize) return -1; return 0; } static off_t dm_mem_fsize(DMResource * f) { return f->rawSize; } static off_t dm_mem_ftell(DMResource * f) { return f->rawOffset; } static BOOL dm_mem_feof(DMResource * f) { // Check for EOF if ((size_t) f->rawOffset <= f->rawSize) return FALSE; else return TRUE; } static int dm_mem_fgetc(DMResource * f) { // Check for EOF if ((size_t) f->rawOffset < f->rawSize) return f->rawData[f->rawOffset++]; 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->rawOffset + length) >= f->rawSize) { nmemb = (f->rawSize - f->rawOffset) / size; length = size * nmemb; } memcpy(buf, f->rawData + f->rawOffset, length); f->rawOffset += length; return nmemb; } static int dm_mem_fputc(int ch, DMResource * f) { // Check for EOF if ((size_t) f->rawOffset < f->rawSize) { f->rawData[f->rawOffset++] = ch; return ch; } else return EOF; } static size_t dm_mem_fwrite(const void *buf, size_t size, size_t nmemb, DMResource * f) { size_t length = (size * nmemb); // Check if we can write the whole chunk if (((size_t) f->rawOffset + length) >= f->rawSize) { nmemb = (f->rawSize - f->rawOffset) / size; length = size * nmemb; } if (length > 0) { memcpy(f->rawData + f->rawOffset, buf, length); f->rawOffset += length; } return nmemb; } #ifdef DM_USE_PACKFS DMResourceOps dfPackFileOps = { "PackFS", dm_mem_freset, dm_mem_ferror, dm_mem_fseek, dm_mem_fsize, dm_mem_ftell, dm_mem_feof, dm_mem_fgetc, NULL, dm_mem_fread, NULL, dm_pack_fopen, dm_pack_fclose, dm_pack_preload, }; #endif DMResourceOps dfMemIOFileOps = { "MemIO", dm_mem_freset, dm_mem_ferror, dm_mem_fseek, dm_mem_fsize, dm_mem_ftell, dm_mem_feof, dm_mem_fgetc, dm_mem_fputc, dm_mem_fread, dm_mem_fwrite, NULL, dmResourceFree, NULL }; /* FS file handling functions. These functions call the actual * functions depending on where the file is located. */ static int dmResourcePreload(DMResource *handle) { int ret = DMERR_OK; // Check if we want to preload raw data? if ((handle->lib->flags & DRF_PRELOAD_RAW) || handle->rops == NULL || handle->rops->load == NULL) { if (handle->flags & DMF_LOADED_RAW) ret = DMERR_OK; else if (handle->fops->preload != NULL) { ret = handle->fops->preload(handle); if (ret == DMERR_OK) handle->flags |= DMF_LOADED_RAW | DMF_PERSIST; } else ret = DMERR_INIT_FAIL; dmfreset(handle); } // Check if resource data is to be preloaded if (ret == DMERR_OK && (handle->lib->flags & DRF_PRELOAD_RES)) { if (handle->flags & DMF_LOADED_RES) ret = DMERR_OK; else if (handle->rops != NULL && handle->rops->load != NULL) { if ((ret = handle->fops->fopen(handle)) == DMERR_OK) { ret = handle->rops->load(handle); handle->fops->fclose(handle); } if (ret == DMERR_OK) handle->flags |= DMF_LOADED_RES; } dmfreset(handle); } return ret; } int dmf_open(DMResourceLib *lib, const char *filename, DMResource **phandle) { DMResource *handle; int ret; // Check master directory for resource if ((*phandle = handle = dmResourceFind(lib, filename)) == NULL) { #ifdef DM_USE_STDIO if (lib->flags & DRF_USE_STDIO) { // Hmm.. does not exist? Fall back to a stdio file *phandle = handle = dmResourceNew(lib, filename, 0, 0); if (handle == NULL) return DMERR_MALLOC; handle->fops = &dfStdioFileOps; } else return DMERR_INIT_FAIL; #else // Stdio not enabled, fail return DMERR_INIT_FAIL; #endif } // Check if the data is preloaded if ((ret = handle->fops->fopen(handle)) == DMERR_OK) { dmResourceRef(handle); if (handle->flags & DMF_TEMPORARY) { handle->flags &= ~DMF_TEMPORARY; dmResourceInsert(lib, handle); } } else if (handle->flags & DMF_TEMPORARY) { dmResourceFree(handle); *phandle = handle = NULL; } dmfreset(handle); return ret; } int dmf_create_memio(DMResourceLib *lib, const char *filename, Uint8 *buf, const size_t size, DMResource **phandle) { DMResource *handle; // Check master directory for resource if ((*phandle = handle = dmResourceFind(lib, filename)) == NULL) { if ((*phandle = handle = dmResourceNew( lib, filename, size, DMF_LOADED_RAW | DMF_UNALLOCATED)) == NULL) return DMERR_MALLOC; handle->fops = &dfMemIOFileOps; handle->rawData = buf; dmResourceInsert(lib, handle); } // Increase refcount dmResourceRef(handle); dmfreset(handle); return DMERR_OK; } #ifdef DM_USE_STDIO int dmf_create_stdio(const char *filename, const char *mode, DMResource **phandle) { DMResource *handle; if ((*phandle = handle = dmResourceNew(NULL, filename, 0, 0)) == NULL) return DMERR_MALLOC; handle->fops = &dfStdioFileOps; handle->fh = fopen(filename, mode); handle->error = dmGetErrno(); if (handle->fh == NULL) { dmResourceFree(handle); return handle->error; } dmResourceRef(handle); return DMERR_OK; } int dmf_create_stdio_stream(FILE *fh, DMResource **phandle) { DMResource *handle; if ((*phandle = handle = dmResourceNew(NULL, "", 0, 0)) == NULL) return DMERR_MALLOC; handle->fops = &dfStdioFHOps; handle->fh = fh; dmResourceRef(handle); return DMERR_OK; } #endif void dmf_close(DMResource * f) { if (f == NULL) return; dmResourceUnref(f); if (f->fops->fclose != NULL) f->fops->fclose(f); } int dmfreset(DMResource *f) { if (f == NULL) return DMERR_NULLPTR; if (f->fops == NULL || f->fops->freset == NULL) return DMERR_OK; return f->fops->freset(f); } int dmferror(DMResource * f) { f->atime = time(NULL); return f->fops->ferror(f); } int dmfseek(DMResource * f, const 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(const void *ptr, size_t size, size_t nmemb, DMResource * f) { f->atime = time(NULL); return f->fops->fwrite(ptr, size, nmemb, f); } char *dmfgets(char *str, int size, DMResource * f) { char *ptr = str, *end = str + size - 1; int c; if (size <= 0) return NULL; while (ptr < end && (c = f->fops->fgetc(f)) != EOF) { *ptr++ = c; if (c == '\n') break; } *ptr = 0; return (ptr > str) ? str : NULL; } int dmResourceRef(DMResource *node) { if (node->lib != NULL) dmMutexLock(node->lib->mutex); node->atime = time(NULL); node->refcount++; if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); return node->refcount; } int dmResourceUnref(DMResource *node) { if (node->lib != NULL) dmMutexLock(node->lib->mutex); node->refcount--; if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); return node->refcount; } #ifdef DM_USE_STDIO static int dmResourcesLoadDirectory(DMResourceLib *lib, const char *path) { int ret = DMERR_OK; struct dirent *dh; DIR *hdir = opendir(path); if (hdir == NULL) return dmGetErrno(); dmMutexLock(lib->mutex); do { DMResource *node = NULL; dh = readdir(hdir); if (dh != NULL) { struct stat sbuf; char *fname = dm_strdup_printf("%s/%s", path, dh->d_name); if (stat(fname, &sbuf) == -1) { ret = dmErrorDBG(dmGetErrno(), "Could not stat() %s, #%d: %s\n", fname, ret, dmErrorStr(ret)); dmFree(fname); goto out; } if (S_ISREG(sbuf.st_mode)) node = dmResourceNew(lib, dh->d_name, sbuf.st_size, 0); } if (node != NULL) { node->fops = &dfStdioFileOps; dmResourceInsert(lib, node); } } while (dh != NULL); out: dmMutexUnlock(lib->mutex); #ifdef __WIN32 #else closedir(hdir); #endif return ret; } #endif /* Resources subsystem initialization and shutdown routines */ int dmResourcesInit(DMResourceLib **plib, const char *filename, const char *path, const int flags, int (*classifier)(DMResource *)) { DMResourceLib *lib; BOOL initialized = FALSE; // Allocate the resource library structure if ((*plib = lib = dmMalloc0(sizeof(DMResourceLib))) == NULL) return DMERR_MALLOC; // Basic data lib->mutex = dmCreateMutex(); lib->flags = flags; lib->resPath = dm_strdup((path != NULL) ? path : DMRES_DATA_PATH); #ifdef DM_USE_PACKFS if (flags & DRF_USE_PACK) { int ret; DMPackEntry *node; lib->packFilename = dm_strdup((filename != NULL) ? filename : DMRES_DATA_PACK); // Initialize PACK, open as read-only ret = dmPackOpen(lib->packFilename, &lib->packFile, TRUE); if (ret != DMERR_OK) { if ((flags & DRF_USE_STDIO) == 0) { return dmErrorDBG(DMERR_INIT_FAIL, "Error opening PACK file '%s', #%d: %s\n", lib->packFilename, ret, dmErrorStr(ret)); } else { // Non-fatal dmErrorDBGMsg( "Failed to open PACK '%s' %d: %s\n", lib->packFilename, ret, dmErrorStr(ret)); } } else { // Initialize resources from a PACK file for (node = lib->packFile->entries; node != NULL; node = node->next) { DMResource *res = dmResourceNew(lib, node->filename, node->size, node->flags & DMF_PACK_MASK); if (res == NULL) { return dmErrorDBG(DMERR_INIT_FAIL, "Could not allocate memory for resource node '%s' [0x%08x], %d bytes.\n", node->filename, node->flags, node->size); } res->fops = &dfPackFileOps; dmResourceInsert(lib, res); } initialized = TRUE; } } #endif #ifdef DM_USE_STDIO if (!initialized && (flags & DRF_USE_STDIO)) { // Initialize resources from a resource directory int ret = dmResourcesLoadDirectory(lib, lib->resPath); if (ret != DMERR_OK) return ret; initialized = TRUE; } #endif if (!initialized) return dmErrorDBG(DMERR_INIT_FAIL, "Failed all resource initialization modes.\n"); // Okay, classify resources if (lib->resources != NULL && classifier != NULL) { DMResource *node; for (node = lib->resources; node != NULL; node = node->next) { int ret = classifier(node); if (ret != DMERR_OK) return ret; } } // Initialization complete return DMERR_OK; } int dmResourcesClose(DMResourceLib *lib) { DMResource *node; if (lib == NULL) return DMERR_NULLPTR; dmMutexLock(lib->mutex); // Shutdown possible subsystems #ifdef DM_USE_PACKFS if (lib->flags & DRF_USE_PACK) { int ret = dmPackClose(lib->packFile); if (ret != DMERR_OK) { dmErrorDBGMsg( "Error closing PACK, #%i: %s\n", ret, dmErrorStr(ret)); } dmFree(lib->packFilename); } #endif // Free resource entries node = lib->resources; while (node != NULL) { DMResource *next = node->next; dmResourceFree(node); node = next; } // Etc. dmFree(lib->resPath); dmMutexUnlock(lib->mutex); dmDestroyMutex(lib->mutex); return DMERR_OK; } int dmResourcesPreload(DMResourceLib *lib, BOOL start, int *loaded, int *total) { int ret = DMERR_OK; dmMutexLock(lib->mutex); // Initialize preloading if (lib->preload == NULL || start) { DMResource *node; lib->preload = lib->resources; *loaded = 0; *total = 0; // Calculate total number of resources to be preloaded if (lib->flags & (DRF_PRELOAD_RAW | DRF_PRELOAD_RES)) { for (node = lib->resources; node != NULL; node = node->next) (*total)++; } } else if (lib->preload != NULL) { // Attempt to preload the resource if ((ret = dmResourcePreload(lib->preload)) != DMERR_OK) { dmErrorDBGMsg("Error preloading '%s', %d: %s\n", lib->preload->filename, ret, dmErrorStr(ret)); goto error; } (*loaded)++; lib->preload = lib->preload->next; } dmMutexUnlock(lib->mutex); return (lib->preload == NULL) ? DMERR_OK : DMERR_PROGRESS; error: dmMutexUnlock(lib->mutex); return ret; } void dmResourcePrune(DMResourceLib *lib, const int agems, int const flags) { DMResource *node; const int stamp = time(NULL); dmMutexLock(lib->mutex); for (node = lib->resources; 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))) { if (((flags & DMPRUNE_ATIME) && stamp - node->atime >= agems) || ((flags & DMPRUNE_MTIME) && stamp - node->mtime >= agems)) { dmResourceFreeResData(node); dmResourceFreeRawData(node); } } } dmMutexUnlock(lib->mutex); } /* Helper resource access routines */ int dmf_read_str(DMResource *f, void *s, size_t l) { return dmfread(s, 1, l, f) == l; } BOOL dmf_read_byte(DMResource *f, Uint8 *val) { int tmp = dmfgetc(f); *val = tmp; return tmp != EOF; } #define DM_DEFINE_FFUNC(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; \ } #include "dmfiletmpl.h" #undef DM_DEFINE_FFUNC