Mercurial > hg > dmlib
view dmres.c @ 382:371edff7dc3d
Fix extern variables to match what are actually declared in dmengine.c
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 17 Oct 2012 18:15:53 +0300 |
parents | 59045853853d |
children | a0160ffdf7e5 |
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> DMResource *dmres_new(DMResourceLib *lib, const char *filename, int flags, size_t size) { DMResource *node = dmMalloc0(sizeof(DMResource)); if (node == NULL) return NULL; node->lib = lib; 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(DMResourceLib *lib, DMResource * node) { if (lib == 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 dmres_delete(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 * dmres_find(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 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 DM_USE_PACKFS static int dm_pack_preload(DMResource *handle) { DMPackEntry *node; int res = DMERR_OK, cres, cdataLeft; z_stream cstream; Uint8 * cbuffer = NULL; if (handle->lib == NULL || handle->lib->packFile == NULL) return DMERR_NULLPTR; // Search PACK nodelist for file if ((node = dm_pack_find(handle->lib->packFile->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(handle->lib->packFile->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, handle->lib->packFile->file); cdataLeft -= cstream.avail_in; cstream.next_in = cbuffer; cres = inflate(&cstream, Z_FULL_FLUSH); } // Cleanup inflateEnd(&(cstream)); error: dmFree(cbuffer); return res; } static void dm_pack_fclose(DMResource * f) { f->dataSize = 0; f->dataOffset = 0; dmFree(f->data); f->data = NULL; } #endif 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; } #ifdef DM_USE_PACKFS 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_pack_fclose, dm_pack_preload }; #endif 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 DM_USE_PACKFS if (handle->lib->flags & DRF_USE_PACK) handle->fops = &dfPackFileOps; #ifdef DM_USE_STDIO else handle->fops = &dfStdioFileOps; #else handle->fops = &dfPackFileOps; #endif #else handle->fops = NULL; #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) || (handle->lib->flags & 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) || (handle->lib->flags & 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(DMResourceLib *lib, const char *filename) { int ret; DMResource *handle; // Check master directory for resource if ((handle = dmres_find(lib, filename)) == NULL) { #ifdef DM_USE_STDIO // Hmm.. does not exist? Fall back to a stdio file handle = dmres_new(lib, filename, 0, 0); if (handle == NULL) return NULL; handle->fops = &dfStdioFileOps; dmres_insert(lib, 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_create_memio(DMResourceLib *lib, const char *filename, Uint8 *buf, size_t len) { DMResource *handle; // Check master directory for resource if ((handle = dmres_find(lib, filename)) == NULL) { // Hmm.. does not exist? Fall back to a stdio file handle = dmres_new(lib, filename, DMF_LOADED_RAW, len); if (handle == NULL) return NULL; handle->fops = &dfMemIOFileOps; handle->data = buf; dmres_insert(lib, handle); } // Increase refcount dmres_ref(handle); return handle; } #ifdef DM_USE_STDIO DMResource * dmf_create_stdio(const char *filename, const char *mode) { DMResource *handle = dmres_new(NULL, 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(NULL, "", 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); } char *dmfgets(char *s, int size, DMResource * f) { char *p = s, c; int n = 0; while ((c = f->fops->fgetc(f)) != EOF) { n++; if (c == '\n') break; else if (n < size - 1) *p++ = c; } *p = 0; return (n > 0) ? s : NULL; } int dmres_ref(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 dmres_unref(DMResource *node) { if (node->lib != NULL) dmMutexLock(node->lib->mutex); node->refcount--; if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); return node->refcount; } #define NADDFLAG(flg, ch) \ do { \ if ((flags & (flg)) && offs < size - 1) \ str[offs++] = ch; \ } while (0) void dmres_flags_to_symbolic(char *str, size_t size, int flags) { size_t offs = 0; NADDFLAG(DMF_PRELOAD_RAW, 'r'); NADDFLAG(DMF_PRELOAD_RES, 'e'); NADDFLAG(DMF_PERSIST, 'p'); NADDFLAG(DMF_STREAM, 's'); if (offs < size) str[offs] = 0; } #undef NADDFLAG int dmres_symbolic_to_flags(const char *str) { int offs, flags; for (flags = offs = 0; str[offs]; offs++) switch (tolower(str[offs])) { case 'r': flags |= DMF_PRELOAD_RAW; break; case 'e': flags |= DMF_PRELOAD_RES; break; case 'p': flags |= DMF_PERSIST; break; case 's': flags |= DMF_STREAM; break; } return flags; } int dmres_load_resfile(DMResourceLib *lib, const char *filename) { int ret = DMERR_OK; char line[256]; FILE *f = fopen(filename, "r"); if (f == NULL) return DMERR_FOPEN; dmMutexLock(lib->mutex); 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++); } } dmMutexUnlock(lib->mutex); fclose(f); return ret; } int dmres_write_resfile(DMResourceLib *lib, const char *filename) { int ret; DMResource *node; FILE *f = fopen(filename, "w"); if (f == NULL) return DMERR_FOPEN; dmMutexLock(lib->mutex); for (node = lib->resources; node != NULL; node = node->next) { char tmp[64]; dmres_flags_to_symbolic(tmp, sizeof(tmp), node->flags); if (fprintf(f, "%s|%s\n", node->filename, tmp) < 0) { ret = DMERR_FWRITE; goto error; } } error: dmMutexUnlock(lib->mutex); fclose(f); return ret; } /* Resources subsystem initialization and shutdown routines */ int dmres_init(DMResourceLib **plib, const char *filename, const char *path, const int flags, int (*classifier)(DMResource *)) { DMResourceLib *lib; // 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); if (flags & DRF_USE_PACK) { #ifdef DM_USE_PACKFS int ret; DMPackEntry *node; lib->packFilename = dm_strdup((filename != NULL) ? filename : DMRES_DATA_PACK); // Initialize PACK, open as read-only ret = dm_pack_open(lib->packFilename, &lib->packFile, TRUE); if (ret != DMERR_OK) { dmError("Error opening PACK file '%s', #%i: %s\n", lib->packFilename, ret, dmErrorStr(ret)); return DMERR_INIT_FAIL; } // Initialize resources from a PACK file for (node = lib->packFile->entries; node != NULL; node = node->next) { DMResource *res = dmres_new(lib, 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(lib, 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", lib->resPath, DMRES_RES_FILE); int ret = dmres_load_resfile(lib, resFilename); dmFree(resFilename); if (ret != DMERR_OK) return DMERR_INIT_FAIL; } // 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 dmres_close(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 res = dm_pack_close(lib->packFile); if (res != DMERR_OK) { dmError("Error closing PACK, #%i: %s\n", res, dmErrorStr(res)); } dmFree(lib->packFilename); } #endif // Free resource entries node = lib->resources; while (node != NULL) { DMResource *next = node->next; dmres_free(node); node = next; } // Etc. dmFree(lib->resPath); dmMutexUnlock(lib->mutex); dmDestroyMutex(lib->mutex); return DMERR_OK; } int dmres_preload(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 for (node = lib->resources; node != NULL; node = node->next) { if ((lib->flags & (DRF_PRELOAD_ALL | DRF_PRELOAD_RES)) || (node->flags & (DMF_PRELOAD_RAW | DMF_PRELOAD_RES))) (*total)++; } } else if (lib->preload != NULL) { // Initialize fops and preload dmf_init_fops(lib->preload); if ((ret = dmf_preload(lib->preload)) != DMERR_OK) 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 dmres_prune(DMResourceLib *lib, int agems, int flags) { DMResource *node; int currtime = 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))) { // 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); } } } dmMutexUnlock(lib->mutex); } /* 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