diff options
author | Paul Gilbert | 2011-02-14 20:37:27 +1100 |
---|---|---|
committer | Paul Gilbert | 2011-02-14 20:37:27 +1100 |
commit | 7042d95cfee6cb2eead81fce497eeaf0da28d297 (patch) | |
tree | 9952920a06dab4052fc97eb8870bcb40f0e6d4d2 /engines/tsage/resources.cpp | |
parent | 97852d473e05b03a1b9a18facc9c2cf0c3ac8a92 (diff) | |
download | scummvm-rg350-7042d95cfee6cb2eead81fce497eeaf0da28d297.tar.gz scummvm-rg350-7042d95cfee6cb2eead81fce497eeaf0da28d297.tar.bz2 scummvm-rg350-7042d95cfee6cb2eead81fce497eeaf0da28d297.zip |
TSAGE: Added the engine in a separate branch
Diffstat (limited to 'engines/tsage/resources.cpp')
-rw-r--r-- | engines/tsage/resources.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/engines/tsage/resources.cpp b/engines/tsage/resources.cpp new file mode 100644 index 0000000000..2df4b54b94 --- /dev/null +++ b/engines/tsage/resources.cpp @@ -0,0 +1,414 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/resources.cpp $ + * $Id: resources.cpp 145 2011-01-08 11:41:39Z dreammaster $ + * + */ + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/stack.h" +#include "common/util.h" +#include "tsage/resources.h" + +namespace tSage { + + +MemoryManager::MemoryManager() { + _memoryPool = new MemoryHeader*[MEMORY_POOL_SIZE]; + Common::set_to(&_memoryPool[0], &_memoryPool[MEMORY_POOL_SIZE], (MemoryHeader *)NULL); +} + +MemoryManager::~MemoryManager() { + for (int i = 0; i < MEMORY_POOL_SIZE; ++i) { + if (_memoryPool[i] != NULL) + free(_memoryPool[i]); + } + delete[] _memoryPool; +} + +uint16 MemoryManager::allocate(uint32 size) { + int idx = 0; + while ((idx < MEMORY_POOL_SIZE) && (_memoryPool[idx] != NULL)) + ++idx; + if (idx == MEMORY_POOL_SIZE) + error("Out of memory handles"); + + // Create the new entry + _memoryPool[idx] = (MemoryHeader *)malloc(sizeof(MemoryHeader) + size); + _memoryPool[idx]->id = MEMORY_ENTRY_ID; + _memoryPool[idx]->index = idx; + _memoryPool[idx]->lockCtr = 0; + _memoryPool[idx]->criticalCtr = 0; + _memoryPool[idx]->tag = 0; + _memoryPool[idx]->size = size; + + // Return it's index + return idx; +} + +byte *MemoryManager::allocate2(uint32 size) { + uint32 idx = allocate(size); + return lock(idx); +} + +byte *MemoryManager::lock(uint32 handle) { + assert((int)handle < MEMORY_POOL_SIZE); + return (byte *)_memoryPool[handle] + sizeof(MemoryHeader); +} + +int MemoryManager::indexOf(const byte *p) { + for (int idx = 0; idx < MEMORY_POOL_SIZE; ++idx) { + if (((byte *)_memoryPool[idx] + sizeof(MemoryHeader)) == p) + return idx; + } + + return -1; +} + +void MemoryManager::deallocate(const byte *p) { + if (!p) + return; + + int idx = indexOf(p); + assert(idx != -1); + if (_memoryPool[idx]->lockCtr-- == 0) { + free(_memoryPool[idx]); + _memoryPool[idx] = NULL; + } +} + +uint32 MemoryManager::getSize(const byte *p) { + int idx = indexOf(p); + assert(idx >= 0); + return _memoryPool[idx]->size; +} + +void MemoryManager::incLocks(const byte *p) { + int idx = indexOf(p); + assert(idx >= 0); + _memoryPool[idx]->lockCtr++; +} + +/*-------------------------------------------------------------------------*/ + +static uint16 bitMasks[4] = {0x1ff, 0x3ff, 0x7ff, 0xfff}; + +uint16 BitReader::readToken() { + assert((numBits >= 9) && (numBits <= 12)); + uint16 result = _remainder; + int bitsLeft = numBits - _bitsLeft; + int bitOffset = _bitsLeft; + _bitsLeft = 0; + + while (bitsLeft >= 0) { + _remainder = readByte(); + result |= _remainder << bitOffset; + bitsLeft -= 8; + bitOffset += 8; + } + + _bitsLeft = -bitsLeft; + _remainder >>= 8 - _bitsLeft; + return result & bitMasks[numBits - 9]; +} + +/*-------------------------------------------------------------------------*/ + +RlbManager::RlbManager(MemoryManager &memManager, const Common::String filename): + _memoryManager(memManager) { + + // If the resource strings list isn't yet loaded, load them + if (_resStrings.size() == 0) { + Common::File f; + if (f.open("tsage.cfg")) { + while (!f.eos()) { + _resStrings.push_back(f.readLine()); + } + f.close(); + } + } + + if (!_file.open(filename)) + error("Missing file %s", filename.c_str()); + + loadIndex(); +} + +RlbManager::~RlbManager() { + _resStrings.clear(); +} + +void RlbManager::loadSection(uint32 fileOffset) { + _resources.clear(); + _file.seek(fileOffset); + _sections.fileOffset = fileOffset; + + if (_file.readUint32BE() != 0x544D492D) + error("Data block is not valid Rlb data"); + + /*uint8 unknown1 = */_file.readByte(); + uint16 numEntries = _file.readByte(); + + for (uint i = 0; i < numEntries; ++i) { + uint16 id = _file.readUint16LE(); + uint16 size = _file.readUint16LE(); + uint16 uncSize = _file.readUint16LE(); + uint8 sizeHi = _file.readByte(); + uint8 type = _file.readByte() >> 5; + assert(type <= 1); + uint32 offset = _file.readUint32LE(); + + ResourceEntry *re = new ResourceEntry(); + re->id = id; + re->fileOffset = offset; + re->isCompressed = type != 0; + re->size = ((sizeHi & 0xF) << 16) | size; + re->uncompressedSize = ((sizeHi & 0xF) << 16) | uncSize; + + _resources.push_back(*re); + } +} + +struct DecodeReference { + uint16 vWord; + uint8 vByte; +}; + +/** + * Gets a resource from the currently loaded section + */ +byte *RlbManager::getResource(uint16 id, bool suppressErrors) { + // Scan for an entry for the given Id + ResourceEntry *re= NULL; + ResourceList::iterator i; + for (i = _resources.begin(); i != _resources.end(); ++i) { + if ((*i).id == id) { + re = &(*i); + break; + } + } + if (!re) { + if (suppressErrors) + return NULL; + error("Could not find resource Id #%d", id); + } + + if (!re->isCompressed) { + // Read in the resource data and return it + byte *dataP = _memoryManager.allocate2(re->size); + _file.seek(_sections.fileOffset + re->fileOffset); + _file.read(dataP, re->size); + + return dataP; + } + + /* + * Decompress the data block + */ + + _file.seek(_sections.fileOffset + re->fileOffset); + Common::ReadStream *compStream = _file.readStream(re->size); + BitReader bitReader(*compStream); + + byte *dataOut = _memoryManager.allocate2(re->uncompressedSize); + byte *destP = dataOut; + uint bytesWritten = 0; + + uint16 ctrCurrent = 0x102, ctrMax = 0x200; + uint16 word_48050 = 0, currentToken = 0, word_48054 =0; + byte byte_49068 = 0, byte_49069 = 0; + DecodeReference table[0x1000]; + Common::Stack<uint16> tokenList; + + for (;;) { + // Get the next decode token + uint16 token = bitReader.readToken(); + + // Handle the token + if (token == 0x101) { + // End of compressed stream + break; + } else if (token == 0x100) { + // Reset bit-rate + bitReader.numBits = 9; + ctrMax = 0x200; + ctrCurrent = 0x102; + + // Set variables with next token + currentToken = word_48050 = bitReader.readToken(); + byte_49069 = byte_49068 = (byte)currentToken; + + ++bytesWritten; + assert(bytesWritten <= re->uncompressedSize); + *destP++ = byte_49069; + } else { + word_48054 = word_48050 = token; + + if (token >= ctrCurrent) { + word_48050 = currentToken; + tokenList.push(byte_49068); + } + + while (word_48050 >= 0x100) { + assert(word_48050 < 0x1000); + tokenList.push(table[word_48050].vByte); + word_48050 = table[word_48050].vWord; + } + + byte_49069 = byte_49068 = (byte)word_48050; + tokenList.push(word_48050); + + // Write out any cached tokens + while (!tokenList.empty()) { + ++bytesWritten; + assert(bytesWritten <= re->uncompressedSize); + *destP++ = tokenList.pop(); + } + + assert(ctrCurrent < 0x1000); + table[ctrCurrent].vByte = byte_49069; + table[ctrCurrent].vWord = currentToken; + ++ctrCurrent; + + currentToken = word_48054; + if ((ctrCurrent >= ctrMax) && (bitReader.numBits != 12)) { + // Move to the next higher bit-rate + ++bitReader.numBits; + ctrMax <<= 1; + } + } + } + + assert(bytesWritten == re->uncompressedSize); + delete compStream; + return dataOut; +} + +/** + * Finds the correct section and loads the specified resource within it + */ +byte *RlbManager::getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors) { + SectionList::iterator i = _sections.begin(); + while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum)) + ++i; + if (i == _sections.end()) { + if (suppressErrors) + return NULL; + error("Unknown resource type %d num %d", resType, resNum); + } + + loadSection((*i).fileOffset); + + return getResource(rlbNum, suppressErrors); +} + +void RlbManager::loadIndex() { + uint16 resNum, configId, fileOffset; + + // Load the root resources section + loadSection(0); + + // Get the single resource from it + const byte *pData = getResource(0); + const byte *p = pData; + + _sections.clear(); + + // Loop through reading the entries + while ((resNum = READ_LE_UINT16(p)) != 0xffff) { + configId = READ_LE_UINT16(p + 2); + fileOffset = READ_LE_UINT16(p + 4); + p += 6; + + SectionEntry *se = new SectionEntry(); + se->resNum = resNum; + se->resType = (ResourceType)(configId & 0x1f); + se->fileOffset = (((configId >> 5) & 0x7ff) << 16) | fileOffset; + + _sections.push_back(*se); + } + + _memoryManager.deallocate(pData); +} + +/** + * Retrieves the specified palette resource and returns it's data + * + * @paletteNum Specefies the palette number + */ +void RlbManager::getPalette(int paletteNum, uint8 *palData, uint *startNum, uint *numEntries) { + // Get the specified palette + byte *dataIn = getResource(RES_PALETTE, 0, paletteNum); + assert(dataIn); + + *startNum = READ_LE_UINT16(dataIn); + *numEntries = READ_LE_UINT16(dataIn + 2); + assert((*startNum < 256) && ((*startNum + *numEntries) <= 256)); + + // Copy over the data + for (uint i = 0; i < *numEntries; ++i) { + *palData++ = dataIn[6 + i * 3]; + *palData++ = dataIn[7 + i * 3]; + *palData++ = dataIn[8 + i * 3]; + *palData++ = 0; + } + + _memoryManager.deallocate(dataIn); +} + +byte *RlbManager::getSubResource(int resNum, int rlbNum, int index, uint *size) { + // Get the specified image set + byte *dataIn = getResource(RES_VISAGE, resNum, rlbNum); + assert(dataIn); + + int numEntries = READ_LE_UINT16(dataIn); + uint32 entryOffset = READ_LE_UINT32(dataIn + 2 + (index - 1) * 4); + uint32 nextOffset = (index == numEntries) ? + _memoryManager.getSize(dataIn) : READ_LE_UINT32(dataIn + 2 + index * 4); + *size = nextOffset - entryOffset; + assert(*size < (1024 * 1024)); + + byte *entry = _memoryManager.allocate2(*size); + Common::copy(&dataIn[entryOffset], &dataIn[nextOffset], entry); + + _memoryManager.deallocate(dataIn); + return entry; +} + +/** + * Retrieves a given message resource, and returns the specified message number + */ +Common::String RlbManager::getMessage(int resNum, int lineNum) { + byte *msgData = getResource(RES_MESSAGE, resNum, 0); + assert(msgData); + + const char *srcP = (const char *)msgData; + while (lineNum-- > 0) + srcP += strlen(srcP) + 1; + + Common::String result(srcP); + _memoryManager.deallocate(msgData); + return result; +} + +} // end of namespace tSage |