From 8ef40c50835b770dd25007d0f2ff6899e97afe5a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Oct 2019 17:13:48 +0100 Subject: [PATCH] - merged most of Blood's resource manager into the file system --- source/blood/src/resource.cpp | 927 ++---------------------- source/blood/src/tile.cpp | 2 - source/common/filesystem/cache.cpp | 138 ++++ source/common/filesystem/filesystem.cpp | 137 +++- source/common/filesystem/filesystem.h | 19 +- source/common/filesystem/resourcefile.h | 33 +- 6 files changed, 399 insertions(+), 857 deletions(-) create mode 100644 source/common/filesystem/cache.cpp diff --git a/source/blood/src/resource.cpp b/source/blood/src/resource.cpp index 571d5e81b..31086a519 100644 --- a/source/blood/src/resource.cpp +++ b/source/blood/src/resource.cpp @@ -29,9 +29,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "crc32_.h" #include "compat.h" #include "cache1d.h" -#ifdef WITHKPLIB -#include "kplib.h" -#endif #include "common_game.h" #include "misc.h" @@ -46,858 +43,88 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -CACHENODE Resource::purgeHead = { NULL, &purgeHead, &purgeHead, 0 }; -#ifdef USE_QHEAP -QHeap *Resource::heap; -#endif -Resource::Resource(void) -{ - dict = NULL; - indexName = NULL; - indexId = NULL; - buffSize = 0; - count = 0; - handle = -1; - crypt = true; -} -Resource::~Resource(void) -{ - if (dict) - { - for (unsigned int i = 0; i < count; i++) - { - if (dict[i].type) - Free(dict[i].type); - if (dict[i].name) - Free(dict[i].name); - if (dict[i].path) - Free(dict[i].path); - } - Free(dict); - dict = NULL; - buffSize = 0; - count = 0; - } - if (handle != -1) - { - kclose(handle); - } -} -void Resource::Init(const char *filename) -{ - RFFHeader header; -#ifdef USE_QHEAP - dassert(heap != NULL); -#endif - - if (filename) - { - handle = kopen4loadfrommod(filename, 0); - if (handle != -1) - { - int nFileLength = kfilelength(handle); - dassert(nFileLength != -1); - if (kread(handle, &header, sizeof(RFFHeader)) != sizeof(RFFHeader) - || memcmp(header.sign, "RFF\x1a", 4)) - { - ThrowError("RFF header corrupted"); - } #if B_BIG_ENDIAN == 1 - header.version = B_LITTLE16(header.version); - header.offset = B_LITTLE32(header.offset); - header.filenum = B_LITTLE32(header.filenum); +// Todo: Hook these up with the resource loader +void ByteSwapQAV(void *p) +{ + QAV *qav = (QAV*)p; + qav->nFrames = B_LITTLE32(qav->nFrames); + qav->ticksPerFrame = B_LITTLE32(qav->ticksPerFrame); + qav->at10 = B_LITTLE32(qav->at10); + qav->x = B_LITTLE32(qav->x); + qav->y = B_LITTLE32(qav->y); + qav->nSprite = B_LITTLE32(qav->nSprite); + for (int i = 0; i < qav->nFrames; i++) + { + FRAMEINFO *pFrame = &qav->frames[i]; + SOUNDINFO *pSound = &pFrame->sound; + pFrame->nCallbackId = B_LITTLE32(pFrame->nCallbackId); + pSound->sound = B_LITTLE32(pSound->sound); + for (int j = 0; j < 8; j++) + { + TILE_FRAME *pTile = &pFrame->tiles[j]; + pTile->picnum = B_LITTLE32(pTile->picnum); + pTile->x = B_LITTLE32(pTile->x); + pTile->y = B_LITTLE32(pTile->y); + pTile->z = B_LITTLE32(pTile->z); + pTile->stat = B_LITTLE32(pTile->stat); + pTile->angle = B_LITTLE16(pTile->angle); + } + } +} + +void ByteSwapSEQ(void *p) +{ + Seq *pSeq = (Seq*)p; + pSeq->version = B_LITTLE16(pSeq->version); + pSeq->nFrames = B_LITTLE16(pSeq->nFrames); + pSeq->at8 = B_LITTLE16(pSeq->at8); + pSeq->ata = B_LITTLE16(pSeq->ata); + pSeq->atc = B_LITTLE32(pSeq->atc); + for (int i = 0; i < pSeq->nFrames; i++) + { + SEQFRAME *pFrame = &pSeq->frames[i]; + BitReader bitReader((char *)pFrame, sizeof(SEQFRAME)); + SEQFRAME swapFrame; + swapFrame.tile = bitReader.readUnsigned(12); + swapFrame.at1_4 = bitReader.readBit(); + swapFrame.at1_5 = bitReader.readBit(); + swapFrame.at1_6 = bitReader.readBit(); + swapFrame.at1_7 = bitReader.readBit(); + swapFrame.at2_0 = bitReader.readUnsigned(8); + swapFrame.at3_0 = bitReader.readUnsigned(8); + swapFrame.at4_0 = bitReader.readSigned(8); + swapFrame.at5_0 = bitReader.readUnsigned(5); + swapFrame.at5_5 = bitReader.readBit(); + swapFrame.at5_6 = bitReader.readBit(); + swapFrame.at5_7 = bitReader.readBit(); + swapFrame.at6_0 = bitReader.readBit(); + swapFrame.at6_1 = bitReader.readBit(); + swapFrame.at6_2 = bitReader.readBit(); + swapFrame.at6_3 = bitReader.readBit(); + swapFrame.at6_4 = bitReader.readBit(); + swapFrame.tile2 = bitReader.readUnsigned(4); + swapFrame.soundRange = bitReader.readUnsigned(4); + swapFrame.surfaceSound = bitReader.readBit(); + swapFrame.reserved = bitReader.readUnsigned(2); + *pFrame = swapFrame; + } +} + +void ByteSwapSFX(void *p) +{ + SFX *pSFX = (SFX*)p; + pSFX->relVol = B_LITTLE32(pSFX->relVol); + pSFX->pitch = B_LITTLE32(pSFX->pitch); + pSFX->pitchRange = B_LITTLE32(pSFX->pitchRange); + pSFX->format = B_LITTLE32(pSFX->format); + pSFX->loopStart = B_LITTLE32(pSFX->loopStart); +} + #endif - switch (header.version & 0xff00) - { - case 0x200: - crypt = 0; - break; - case 0x300: - crypt = 1; - break; - default: - ThrowError("Unknown RFF version"); - break; - } - count = header.filenum; - if (count) - { - buffSize = 1; - while (count * 2 >= buffSize) - { - buffSize *= 2; - } - dict = (DICTNODE*)Alloc(buffSize * sizeof(DICTNODE)); - memset(dict, 0, buffSize * sizeof(DICTNODE)); - DICTNODE_FILE *tdict = (DICTNODE_FILE*)Alloc(count*sizeof(DICTNODE_FILE)); - int r = klseek(handle, header.offset, SEEK_SET); - dassert(r != -1); - if ((uint32_t)kread(handle, tdict, count * sizeof(DICTNODE_FILE)) != count*sizeof(DICTNODE_FILE)) - { - ThrowError("RFF dictionary corrupted"); - } - if (crypt) - { - Crypt(tdict, count * sizeof(DICTNODE_FILE), - header.offset + (header.version & 0xff) * header.offset); - } - for (unsigned int i = 0; i < count; i++) - { - dict[i].offset = B_LITTLE32(tdict[i].offset); - dict[i].size = B_LITTLE32(tdict[i].size); - dict[i].flags = tdict[i].flags; - int nTypeLength = strnlen(tdict[i].type, 3); - int nNameLength = strnlen(tdict[i].name, 8); - dict[i].type = (char*)Alloc(nTypeLength+1); - dict[i].name = (char*)Alloc(nNameLength+1); - strncpy(dict[i].type, tdict[i].type, min(3, nTypeLength)); - strncpy(dict[i].name, tdict[i].name, min(8, nNameLength)); - dict[i].path = NULL; - dict[i].type[nTypeLength] = 0; - dict[i].name[nNameLength] = 0; - dict[i].id = B_LITTLE32(tdict[i].id); - dict[i].buffer = NULL; - } - Free(tdict); - } - } - } - if (!dict) - { - buffSize = 16; - dict = (DICTNODE*)Alloc(buffSize * sizeof(DICTNODE)); - memset(dict, 0, buffSize * sizeof(DICTNODE)); - } - Reindex(); - for (unsigned int i = 0; i < count; i++) - { - if (dict[i].flags & DICT_LOCK) - { - Lock(&dict[i]); - } - } - for (unsigned int i = 0; i < count; i++) - { - if (dict[i].flags & DICT_LOAD) - { - Load(&dict[i]); - } - } -} -void Resource::Flush(CACHENODE *h) -{ - if (h->ptr) - { -#ifdef USE_QHEAP - heap->Free(h->ptr); -#else - delete[] (char*)h->ptr; -#endif - - h->ptr = NULL; - if (h->lockCount == 0) - { - RemoveMRU(h); - return; - } - h->lockCount = 0; - } -} - -void Resource::Purge(void) -{ - for (unsigned int i = 0; i < count; i++) - { - if (dict[i].ptr) - { - Flush((CACHENODE *)&dict[i]); - } - } -} - -DICTNODE **Resource::Probe(const char *fname, const char *type) -{ - char name[BMAX_PATH]; - dassert(indexName != NULL); - memset(name, 0, sizeof(name)); - strcpy(name, type); - strcat(name, fname); - dassert(dict != NULL); - unsigned int hash = Bcrc32(name, strlen(name), 0) & (buffSize - 1); - unsigned int i = hash; - do - { - if (!indexName[i]) - { - return &indexName[i]; - } - if (!strcmp((*indexName[i]).type, type) - && !strcmp((*indexName[i]).name, fname)) - { - return &indexName[i]; - } - if (++i == buffSize) - { - i = 0; - } - } while (i != hash); - ThrowError("Linear probe failed to find match or unused node!"); - return NULL; -} - -DICTNODE **Resource::Probe(unsigned int id, const char *type) -{ - struct { - int id; - char type[BMAX_PATH]; - } name; - dassert(indexName != NULL); - memset(&name, 0, sizeof(name)); - strcpy(name.type, type); - name.id = id; - dassert(dict != NULL); - unsigned int hash = Bcrc32(&name, strlen(name.type)+sizeof(name.id), 0) & (buffSize - 1); - unsigned int i = hash; - do - { - if (!indexId[i]) - { - return &indexId[i]; - } - if (!strcmp((*indexId[i]).type, type) - && (*indexId[i]).id == id) - { - return &indexId[i]; - } - if (++i == buffSize) - { - i = 0; - } - } while (i != hash); - ThrowError("Linear probe failed to find match or unused node!"); - return NULL; -} - -void Resource::Reindex(void) -{ - if (indexName) - { - Free(indexName); - } - indexName = (DICTNODE **)Alloc(buffSize * sizeof(DICTNODE*)); - memset(indexName, 0, buffSize * sizeof(DICTNODE*)); - for (unsigned int i = 0; i < count; i++) - { - DICTNODE **node = Probe(dict[i].name, dict[i].type); - *node = &dict[i]; - } - - if (indexId) - { - Free(indexId); - } - indexId = (DICTNODE **)Alloc(buffSize * sizeof(DICTNODE*)); - memset(indexId, 0, buffSize * sizeof(DICTNODE*)); - for (unsigned int i = 0; i < count; i++) - { - if (dict[i].flags & DICT_ID) - { - DICTNODE **node = Probe(dict[i].id, dict[i].type); - *node = &dict[i]; - } - } -} - -void Resource::Grow(void) -{ - buffSize *= 2; - void *p = Alloc(buffSize * sizeof(DICTNODE)); - memset(p, 0, buffSize * sizeof(DICTNODE)); - memcpy(p, dict, count * sizeof(DICTNODE)); - Free(dict); - dict = (DICTNODE*)p; - Reindex(); -} - -void Resource::AddExternalResource(const char *name, const char *type, int id, int flags, const char *pzDirectory) -{ - char name2[BMAX_PATH], type2[BMAX_PATH], filename[BMAX_PATH], path[BMAX_PATH]; - - if (Bstrlen(type) > 0) - Bsnprintf(filename, BMAX_PATH-1, "%s.%s", name, type); - else - Bsnprintf(filename, BMAX_PATH-1, "%s", name); - - if (pzDirectory) - Bsnprintf(path, BMAX_PATH-1, "%s/%s", pzDirectory, filename); - else - Bstrncpy(path, filename, BMAX_PATH-1); - - int fhandle = kopen4loadfrommod(filename, 0); - if (fhandle == -1) - return; - int size = kfilelength(fhandle); - kclose(fhandle); - strcpy(name2, name); - strcpy(type2, type); - Bstrupr(name2); - Bstrupr(type2); - dassert(dict != NULL); - DICTNODE **index = Probe(name2, type2); - dassert(index != NULL); - DICTNODE *node = *index; - if (node && (node->flags & DICT_EXTERNAL)) - return; - if (!node) - { - if (2 * count >= buffSize) - { - Grow(); - } - node = &dict[count++]; - index = Probe(name2, type2); - *index = node; - if (node->type) - { - Free(node->type); - node->type = NULL; - } - if (node->name) - { - Free(node->name); - node->name = NULL; - } - if (node->path) - { - Free(node->path); - node->path = NULL; - } - int nTypeLength = strlen(type2); - int nNameLength = strlen(name2); - int nPathLength = strlen(path); - node->type = (char*)Alloc(nTypeLength+1); - node->name = (char*)Alloc(nNameLength+1); - node->path = (char*)Alloc(nPathLength+1); - strcpy(node->type, type2); - strcpy(node->name, name2); - strcpy(node->path, path); - } - node->size = size; - node->flags = DICT_EXTERNAL | flags; - node->buffer = NULL; - Flush(node); - if (id >= 0) - { - index = Probe(id, type2); - dassert(index != NULL); - DICTNODE *node = *index; - if (!node) - { - if (2 * count >= buffSize) - { - Grow(); - } - node = &dict[count++]; - index = Probe(id, type2); - *index = node; - } - if (node->type) - { - Free(node->type); - node->type = NULL; - } - if (node->name) - { - Free(node->name); - node->name = NULL; - } - if (node->path) - { - Free(node->path); - node->path = NULL; - } - int nTypeLength = strlen(type2); - int nNameLength = strlen(name2); - int nPathLength = strlen(path); - node->type = (char*)Alloc(nTypeLength+1); - node->name = (char*)Alloc(nNameLength+1); - node->path = (char*)Alloc(nPathLength+1); - strcpy(node->type, type2); - strcpy(node->name, name2); - strcpy(node->path, path); - node->id = id; - node->size = size; - node->flags = DICT_EXTERNAL | flags; - node->buffer = NULL; - Flush(node); - } -} - -void Resource::AddFromBuffer(const char* name, const char* type, char* data, int size, int id, int flags) -{ - char name2[BMAX_PATH], type2[BMAX_PATH]; - - char *pHeapData = (char*)Alloc(size); - if (!pHeapData) - return; - Bmemcpy(pHeapData, data, size); - strcpy(name2, name); - strcpy(type2, type); - Bstrupr(name2); - Bstrupr(type2); - dassert(dict != NULL); - DICTNODE **index = Probe(name2, type2); - dassert(index != NULL); - DICTNODE *node = *index; - if (!node) - { - if (2 * count >= buffSize) - { - Grow(); - } - node = &dict[count++]; - index = Probe(name2, type2); - *index = node; - if (node->type) - { - Free(node->type); - node->type = NULL; - } - if (node->name) - { - Free(node->name); - node->name = NULL; - } - if (node->path) - { - Free(node->path); - node->path = NULL; - } - int nTypeLength = strlen(type2); - int nNameLength = strlen(name2); - node->type = (char*)Alloc(nTypeLength+1); - node->name = (char*)Alloc(nNameLength+1); - strcpy(node->type, type2); - strcpy(node->name, name2); - } - node->size = size; - node->flags = DICT_BUFFER | flags; - node->buffer = pHeapData; - Flush(node); - if (id >= 0) - { - index = Probe(id, type2); - dassert(index != NULL); - DICTNODE *node = *index; - if (!node) - { - if (2 * count >= buffSize) - { - Grow(); - } - node = &dict[count++]; - index = Probe(id, type2); - *index = node; - } - if (node->type) - { - Free(node->type); - node->type = NULL; - } - if (node->name) - { - Free(node->name); - node->name = NULL; - } - if (node->path) - { - Free(node->path); - node->path = NULL; - } - int nTypeLength = strlen(type2); - int nNameLength = strlen(name2); - node->type = (char*)Alloc(nTypeLength+1); - node->name = (char*)Alloc(nNameLength+1); - strcpy(node->type, type2); - strcpy(node->name, name2); - node->id = id; - node->size = size; - node->flags = DICT_BUFFER | flags; - node->buffer = pHeapData; - Flush(node); - } -} - -void *Resource::Alloc(int nSize) -{ -#ifdef USE_QHEAP - dassert(heap != NULL); - dassert(nSize != 0); - void *p = heap->Alloc(nSize); - if (p) - { - return p; - } - for (CACHENODE *node = purgeHead.next; node != &purgeHead; node = node->next) - { - dassert(node->lockCount == 0); - dassert(node->ptr != NULL); - int nFree = heap->Free(node->ptr); - node->ptr = NULL; - RemoveMRU(node); - if (nSize <= nFree) - { - p = Alloc(nSize); - dassert(p != NULL); - return p; - } - } - ThrowError("Out of memory!"); - return NULL; -#else - dassert(nSize != 0); - void* p = new char[nSize]; - if (p) - { - return p; - } - for (CACHENODE *node = purgeHead.next; node != &purgeHead; node = node->next) - { - dassert(node->lockCount == 0); - dassert(node->ptr != NULL); - delete[] (char*)node->ptr; - node->ptr = NULL; - RemoveMRU(node); - p = new char[nSize]; - if (p) - return p; - } - ThrowError("Out of memory!"); - return NULL; -#endif -} - -void Resource::Free(void *p) -{ -#ifdef USE_QHEAP - dassert(heap != NULL); - dassert(p != NULL); - heap->Free(p); -#else - dassert(p != NULL); - delete[] (char*)p; -#endif -} - -DICTNODE *Resource::Lookup(const char *name, const char *type) -{ - char name2[BMAX_PATH], type2[BMAX_PATH]; - dassert(name != NULL); - dassert(type != NULL); - //if (strlen(name) > 8 || strlen(type) > 3) return NULL; - // Try to load external resource first - AddExternalResource(name, type); - strcpy(name2, name); - strcpy(type2, type); - Bstrupr(type2); - Bstrupr(name2); - return *Probe(name2, type2); -} - -DICTNODE *Resource::Lookup(unsigned int id, const char *type) -{ - char type2[BMAX_PATH]; - dassert(type != NULL); - //if (strlen(type) > 3) return NULL; - strcpy(type2, type); - Bstrupr(type2); - return *Probe(id, type2); -} - -void Resource::Read(DICTNODE *n) -{ - dassert(n != NULL); - Read(n, n->ptr); -} - -void Resource::Read(DICTNODE *n, void *p) -{ - char filename[BMAX_PATH]; - dassert(n != NULL); - if (n->flags & DICT_EXTERNAL) - { - if (n->path) - Bstrncpy(filename, n->path, BMAX_PATH-1); - else - Bsnprintf(filename, BMAX_PATH-1, "%s.%s", n->name, n->type); - int fhandle = kopen4loadfrommod(filename, 0); - if (fhandle == -1 || (uint32_t)kread(fhandle, p, n->size) != n->size) - { - ThrowError("Error reading external resource (%i)", errno); - } - kclose(fhandle); - } - else if (n->flags & DICT_BUFFER) - { - Bmemcpy(p, n->buffer, n->size); - } - else - { - int r = klseek(handle, n->offset, SEEK_SET); - if (r == -1) - { - ThrowError("Error seeking to resource!"); - } - if ((uint32_t)kread(handle, p, n->size) != n->size) - { - ThrowError("Error loading resource!"); - } - if (n->flags & DICT_CRYPT) - { - int size; - if (n->size > 0x100) - { - size = 0x100; - } - else - { - size = n->size; - } - Crypt(n->ptr, size, 0); - } -#if B_BIG_ENDIAN == 1 - if (!Bstrcmp(n->type, "QAV")) - { - QAV *qav = (QAV*)p; - qav->nFrames = B_LITTLE32(qav->nFrames); - qav->ticksPerFrame = B_LITTLE32(qav->ticksPerFrame); - qav->at10 = B_LITTLE32(qav->at10); - qav->x = B_LITTLE32(qav->x); - qav->y = B_LITTLE32(qav->y); - qav->nSprite = B_LITTLE32(qav->nSprite); - for (int i = 0; i < qav->nFrames; i++) - { - FRAMEINFO *pFrame = &qav->frames[i]; - SOUNDINFO *pSound = &pFrame->sound; - pFrame->nCallbackId = B_LITTLE32(pFrame->nCallbackId); - pSound->sound = B_LITTLE32(pSound->sound); - for (int j = 0; j < 8; j++) - { - TILE_FRAME *pTile = &pFrame->tiles[j]; - pTile->picnum = B_LITTLE32(pTile->picnum); - pTile->x = B_LITTLE32(pTile->x); - pTile->y = B_LITTLE32(pTile->y); - pTile->z = B_LITTLE32(pTile->z); - pTile->stat = B_LITTLE32(pTile->stat); - pTile->angle = B_LITTLE16(pTile->angle); - } - } - } - else if (!Bstrcmp(n->type, "SEQ")) - { - Seq *pSeq = (Seq*)p; - pSeq->version = B_LITTLE16(pSeq->version); - pSeq->nFrames = B_LITTLE16(pSeq->nFrames); - pSeq->at8 = B_LITTLE16(pSeq->at8); - pSeq->ata = B_LITTLE16(pSeq->ata); - pSeq->atc = B_LITTLE32(pSeq->atc); - for (int i = 0; i < pSeq->nFrames; i++) - { - SEQFRAME *pFrame = &pSeq->frames[i]; - BitReader bitReader((char *)pFrame, sizeof(SEQFRAME)); - SEQFRAME swapFrame; - swapFrame.tile = bitReader.readUnsigned(12); - swapFrame.at1_4 = bitReader.readBit(); - swapFrame.at1_5 = bitReader.readBit(); - swapFrame.at1_6 = bitReader.readBit(); - swapFrame.at1_7 = bitReader.readBit(); - swapFrame.at2_0 = bitReader.readUnsigned(8); - swapFrame.at3_0 = bitReader.readUnsigned(8); - swapFrame.at4_0 = bitReader.readSigned(8); - swapFrame.at5_0 = bitReader.readUnsigned(5); - swapFrame.at5_5 = bitReader.readBit(); - swapFrame.at5_6 = bitReader.readBit(); - swapFrame.at5_7 = bitReader.readBit(); - swapFrame.at6_0 = bitReader.readBit(); - swapFrame.at6_1 = bitReader.readBit(); - swapFrame.at6_2 = bitReader.readBit(); - swapFrame.at6_3 = bitReader.readBit(); - swapFrame.at6_4 = bitReader.readBit(); - swapFrame.tile2 = bitReader.readUnsigned(4); - swapFrame.soundRange = bitReader.readUnsigned(4); - swapFrame.surfaceSound = bitReader.readBit(); - swapFrame.reserved = bitReader.readUnsigned(2); - *pFrame = swapFrame; - } - } - else if (!Bstrcmp(n->type, "SFX")) - { - SFX *pSFX = (SFX*)p; - pSFX->relVol = B_LITTLE32(pSFX->relVol); - pSFX->pitch = B_LITTLE32(pSFX->pitch); - pSFX->pitchRange = B_LITTLE32(pSFX->pitchRange); - pSFX->format = B_LITTLE32(pSFX->format); - pSFX->loopStart = B_LITTLE32(pSFX->loopStart); - } -#endif - } -} - -void *Resource::Load(DICTNODE *h) -{ - dassert(h != NULL); - if (h->ptr) - { - if (!h->lockCount) - { - RemoveMRU(h); - - h->prev = purgeHead.prev; - purgeHead.prev->next = h; - h->next = &purgeHead; - purgeHead.prev = h; - } - } - else - { - h->ptr = Alloc(h->size); - Read(h); - - h->prev = purgeHead.prev; - purgeHead.prev->next = h; - h->next = &purgeHead; - purgeHead.prev = h; - } - return h->ptr; -} - -void *Resource::Load(DICTNODE *h, void *p) -{ - dassert(h != NULL); - if (p) - { - Read(h, p); - } - return p; -} - -void *Resource::Lock(DICTNODE *h) -{ - dassert(h != NULL); - if (h->ptr) - { - if (h->lockCount == 0) - { - RemoveMRU(h); - } - } - else - { - h->ptr = Alloc(h->size); - Read(h); - } - - h->lockCount++; - return h->ptr; -} - -void Resource::Unlock(DICTNODE *h) -{ - dassert(h != NULL); - dassert(h->ptr != NULL); - if (h->lockCount > 0) - { - h->lockCount--; - if (h->lockCount == 0) - { - h->prev = purgeHead.prev; - purgeHead.prev->next = h; - h->next = &purgeHead; - purgeHead.prev = h; - } - } -} - -void Resource::Crypt(void *p, int length, unsigned short key) -{ - char *cp = (char*)p; - for (int i = 0; i < length; i++, key++) - { - cp[i] ^= (key >> 1); - } -} - -void Resource::RemoveMRU(CACHENODE *h) -{ - h->prev->next = h->next; - h->next->prev = h->prev; -} - -void Resource::FNAddFiles(fnlist_t * fnlist, const char *pattern) -{ -} - -void Resource::PurgeCache(void) -{ -#ifndef USE_QHEAP - for (CACHENODE *node = purgeHead.next; node != &purgeHead; node = node->next) - { - DICTNODE *pDict = (DICTNODE*)node; - if (!(pDict->flags & DICT_LOAD)) - { - dassert(pDict->lockCount == 0); - dassert(pDict->ptr != NULL); - Free(pDict->ptr); - pDict->ptr = NULL; - RemoveMRU(pDict); - } - } -#endif -} - -void Resource::PrecacheSounds(void) -{ - for (unsigned int i = 0; i < count; i++) - { - DICTNODE *pNode = &dict[i]; - if ((!strcmp(pNode->type, "RAW") || !strcmp(pNode->type, "SFX")) && !pNode->ptr) - { - Load(pNode); - gameHandleEvents(); - } - } -} - -void Resource::RemoveNode(DICTNODE* pNode) -{ - Flush(pNode); - if (pNode->name) - { - Free(pNode->name); - pNode->name = NULL; - } - if (pNode->type) - { - Free(pNode->type); - pNode->type = NULL; - } - if (pNode->path) - { - Free(pNode->path); - pNode->path = NULL; - } - *pNode = dict[--count]; - Bmemset(&dict[count], 0, sizeof(DICTNODE)); - if (pNode->ptr && !pNode->lockCount) - { - pNode->prev->next = pNode; - pNode->next->prev = pNode; - } - Reindex(); -} END_BLD_NS diff --git a/source/blood/src/tile.cpp b/source/blood/src/tile.cpp index c580cd83b..c700fbb76 100644 --- a/source/blood/src/tile.cpp +++ b/source/blood/src/tile.cpp @@ -64,8 +64,6 @@ void qloadvoxel(int32_t nVoxel) } } -CACHENODE tileNode[kMaxTiles]; - bool artLoaded = false; int nTileFiles = 0; diff --git a/source/common/filesystem/cache.cpp b/source/common/filesystem/cache.cpp new file mode 100644 index 000000000..50df3eebb --- /dev/null +++ b/source/common/filesystem/cache.cpp @@ -0,0 +1,138 @@ +CACHENODE Resource::purgeHead = { NULL, &purgeHead, &purgeHead, 0 }; + + +void *Resource::Lock(DICTNODE *h) +{ + dassert(h != NULL); + if (h->ptr) + { + if (h->lockCount == 0) + { + RemoveMRU(h); + } + } + else + { + h->ptr = Alloc(h->size); + Read(h); + } + + h->lockCount++; + return h->ptr; +} + +void Resource::Unlock(DICTNODE *h) +{ + dassert(h != NULL); + dassert(h->ptr != NULL); + if (h->lockCount > 0) + { + h->lockCount--; + if (h->lockCount == 0) + { + h->prev = purgeHead.prev; + purgeHead.prev->next = h; + h->next = &purgeHead; + purgeHead.prev = h; + } + } +} + + +void Resource::Flush(CACHENODE *h) +{ + if (h->ptr) + { +#ifdef USE_QHEAP + heap->Free(h->ptr); +#else + delete[] (char*)h->ptr; +#endif + + h->ptr = NULL; + if (h->lockCount == 0) + { + RemoveMRU(h); + return; + } + h->lockCount = 0; + } +} + +void Resource::Purge(void) +{ + for (unsigned int i = 0; i < count; i++) + { + if (dict[i].ptr) + { + Flush((CACHENODE *)&dict[i]); + } + } +} + +void Resource::PurgeCache(void) +{ +#ifndef USE_QHEAP + for (CACHENODE *node = purgeHead.next; node != &purgeHead; node = node->next) + { + DICTNODE *pDict = (DICTNODE*)node; + if (!(pDict->flags & DICT_LOAD)) + { + dassert(pDict->lockCount == 0); + dassert(pDict->ptr != NULL); + Free(pDict->ptr); + pDict->ptr = NULL; + RemoveMRU(pDict); + } + } +#endif +} + +void *Resource::Load(DICTNODE *h) +{ + dassert(h != NULL); + if (h->ptr) + { + if (!h->lockCount) + { + RemoveMRU(h); + + h->prev = purgeHead.prev; + purgeHead.prev->next = h; + h->next = &purgeHead; + purgeHead.prev = h; + } + } + else + { + h->ptr = Alloc(h->size); + Read(h); + + h->prev = purgeHead.prev; + purgeHead.prev->next = h; + h->next = &purgeHead; + purgeHead.prev = h; + } + return h->ptr; +} + + +void Resource::RemoveMRU(CACHENODE *h) +{ + h->prev->next = h->next; + h->next->prev = h->prev; +} + + +void Resource::PrecacheSounds(void) +{ + for (unsigned int i = 0; i < count; i++) + { + DICTNODE *pNode = &dict[i]; + if ((!strcmp(pNode->type, "RAW") || !strcmp(pNode->type, "SFX")) && !pNode->ptr) + { + Load(pNode); + gameHandleEvents(); + } + } +} diff --git a/source/common/filesystem/filesystem.cpp b/source/common/filesystem/filesystem.cpp index ac9d62a48..9fc559e60 100644 --- a/source/common/filesystem/filesystem.cpp +++ b/source/common/filesystem/filesystem.cpp @@ -79,9 +79,14 @@ FileSystem::~FileSystem () void FileSystem::DeleteAll () { - FileInfo.Clear(); NumEntries = 0; + // explicitly delete all manually added lumps. + for (auto &frec : FileInfo) + { + if (frec.rfnum == -1) delete frec.lump; + } + FileInfo.Clear(); for (int i = Files.Size() - 1; i >= 0; --i) { delete Files[i]; @@ -428,6 +433,26 @@ void FileSystem::InitHashChains (void) } } +void FileSystem::AddLump(FResourceLump *lump) +{ + FileRecord rec = { -1, lump}; + FileInfo.Push(rec); + + for (int l = 0; l < NumLookupModes; l++) + { + int hash; + if (l != (int)ELookupMode::IdWithType && lump->LumpName[l] != NAME_None) + { + hash = int(lump->LumpName[l]) % NumEntries; + } + else if (lump->ResourceId > 0) + { + hash = int(lump->ResourceId) % NumEntries; + } + NextFileIndex[l][hash] = FirstFileIndex[l][hash]; + FirstFileIndex[l][hash] = i; + } +} //========================================================================== // @@ -649,6 +674,27 @@ const void *FileSystem::Get(int lump) return lump->Get(); } +//========================================================================== +// +// Stand-ins for Blood's resource class +// +//========================================================================== + +void *FileSystem::Lock(FResourceLump *lump) +{ + if (lump) return lump->Lock(); +} + +void FileSystem::Unlock(FResourceLump *lump) +{ + if (lump) return lump->Unlock(); +} + +void FileSystem::Load(FResourceLump *lump) +{ + if (lump) return lump->Get(); +} + //========================================================================== // // ReadFile - variant 2 @@ -801,6 +847,95 @@ const char *FileSystem::GetResourceFileFullName (int rfnum) const noexcept return Files[rfnum]->FileName; } +//========================================================================== +// +// AddFromBuffer +// +// Adds an in-memory resource to the virtual directory +// +//========================================================================== + +void FileSystem::AddFromBuffer(const char* name, const char* type, char* data, int size, int id, int flags) +{ + FStringf fullname("%s.%s", name, type); + auto newlump = new FMemoryLump(data, size); + newlump->LumpNameSetup(fullname); + newlump->ResourceId = id; + AddLump(newlump); +} + +//========================================================================== +// +// Read +// +// Reads lump into buffer (simulate Blood interface) +// +//========================================================================== + +void FileSystem::Read(FResourceLump *n, void *p) +{ + if (!n || !p) return; + auto r = n->Get(); + memcpy(p, r, n->Size()); +} + +//========================================================================== +// +// Blood style lookup functions +// +//========================================================================== + +FResourceLump *FileSystem::Lookup(const char *name, const char *type) +{ + FStringf fname("%s.%s", name, type); + auto lump = FindFile(fname); + if (lump >= 0) return FileInfo[lump].lump; +} + +FResourceLump *FileSystem::Lookup(unsigned int id, const char *type) +{ + auto lump = FindResource(id, type); + auto lump = FindFile(fname); + if (lump >= 0) return FileInfo[lump].lump; +} + +//========================================================================== +// +// Clones an existing resource with different properties +// +//========================================================================== + +void FileSystem::AddExternalResource(const char *name, const char *type, int id, int flags, const char *pzDirectory) +{ + FString name2, type2, filename, path; + + if (strlen(type) > 0) + filename.Format("%s.%s", name, type); + else + filename.Format("%s", name); + + if (pzDirectory) + path.Format("%s/%s", pzDirectory, filename); + else + path = filename; + + // The old code said 'filename' and ignored the path, this looked like a bug. + auto lump = FindFile(path); + if (lump < 0) return; // Does not exist. + + // Check if a lump with this name already exists. + // Blood does not allow re-replacing external resources. + auto prevlump = FindFile(filename); + if (prevlump >= 0 && FileInfo[prevlump].rfnum == -1) return; + + // Create a clone of the resource to give it new lookup properties. + auto newlump = new FClonedLump(FileInfo[lump].lump); + newlump->LumpNameSetup(filename); + newlump->ResourceId = id; + if (flags & DICT_LOCK) newlump->Lock(); + else if (flags & DICT_LOAD) newlump->Get(); + AddLump(newlump); +} //========================================================================== // diff --git a/source/common/filesystem/filesystem.h b/source/common/filesystem/filesystem.h index 016f05cbc..f4de4df29 100644 --- a/source/common/filesystem/filesystem.h +++ b/source/common/filesystem/filesystem.h @@ -49,6 +49,11 @@ struct FolderEntry unsigned lumpnum; }; +enum DICTFLAGS { + DICT_LOAD = 4, + DICT_LOCK = 8, +}; + enum class ELookupMode // Todo: Merge with FResourceLump::ENameType { FullName, @@ -94,6 +99,8 @@ public: int FindResource (int resid, const char *type, int filenum = -1) const noexcept; int GetResource (int resid, const char *type, int filenum = -1) const; // Like FindFile, but throws an exception when it cannot find what it looks for. + void AddFromBuffer(const char* name, const char* type, char* data, int size, int id, int flags); + TArray GetFileData(int file, int pad = 0); // reads file into a writable buffer and optionally adds some padding at the end. (FileData isn't writable!) FileData ReadFile (int file); @@ -102,6 +109,16 @@ public: const void *Lock(int lump); void Unlock(bool mayfree = false); void *Get(int lump); + + // These are designed to be stand-ins for Blood's resource class. + static void *Lock(FResourceLump *lump); + static void Unlock(FResourceLump *lump); + static void *Load(FResourceLump *lump); + static void Read(FResourceLump *lump) { Load(lump); } + static void Read(FResourceLump *n, void *p); + FResourceLump *Lookup(const char *name, const char *type); + FResourceLump *Lookup(unsigned int id, const char *type); + void AddExternalResource(const char *name, const char *type, int id, int flags, const char *pzDirectory); FileReader OpenFileReader(int file); // opens a reader that redirects to the containing file's one. FileReader ReopenFileReader(int file, bool alwayscache = false); // opens an independent reader. @@ -138,7 +155,7 @@ protected: uint32_t *NextFileIndex[NumLookupModes]; uint32_t NumFiles = 0; // Not necessarily the same as FileInfo.Size() - uint32_t NumEntries; + uint32_t NumEntries; // Hash modulus. Can be smaller than NumFiles if things get added at run time. void InitHashChains (); // [RH] Set up the lumpinfo hashing diff --git a/source/common/filesystem/resourcefile.h b/source/common/filesystem/resourcefile.h index 4b62ee180..b76c86cf4 100644 --- a/source/common/filesystem/resourcefile.h +++ b/source/common/filesystem/resourcefile.h @@ -74,9 +74,9 @@ struct FResourceLump void LumpNameSetup(FString iname); virtual FCompressedBuffer GetRawData(); - void *Lock(); // validates the cache and increases the refcount. - void Unlock(bool freeunrefd = false); // recreases the refcount and optionally frees the buffer - void *Get(); // validates the cache and returns a pointer without locking + virtual void *Lock(); // validates the cache and increases the refcount. + virtual void Unlock(bool freeunrefd = false); // recreases the refcount and optionally frees the buffer + virtual void *Get(); // validates the cache and returns a pointer without locking // Wrappers for emulating Blood's resource system unsigned Size() const{ return LumpSize; } @@ -165,6 +165,33 @@ struct FExternalLump : public FResourceLump }; +struct FMemoryLump : public FResourceLump +{ + FMemoryLump(const void *data, int length) + { + Cache.Resize(length); + memcpy(Cache.Data(), data, length); + } + virtual int ValidateCache() override + { + RefCount = INT_MAX / 2; // Make sure it never counts down to 0 by resetting it to something high each time it is used. + return 1; + } +} + +struct FClonedLump : public FResourceLump +{ + FResourceLump *parent; + FClonedLump(FResourceLump *lump) + { + parent = lump; + } + void *Lock() { return parent->Lock(); } + void Unlock(bool mayfree) override { parent->Unlock(mayfree); } + void *Get() { return parent->Get(); } + void ValidateCache() override { parent->ValidateCache(); } +} + #endif