diff options
Diffstat (limited to 'engines/chewy/resource.cpp')
-rw-r--r-- | engines/chewy/resource.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/engines/chewy/resource.cpp b/engines/chewy/resource.cpp new file mode 100644 index 0000000000..e112d98cdb --- /dev/null +++ b/engines/chewy/resource.cpp @@ -0,0 +1,314 @@ +/* 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. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/substream.h" +#include "common/textconsole.h" +#include "graphics/pixelformat.h" +#include "graphics/surface.h" + +#include "chewy/chewy.h" +#include "chewy/resource.h" + +namespace Chewy { + +// Resource files - TODO: +// ====================== +// back/episode1.gep +// cut/blende.rnd +// misc/exit.eib +// misc/inventar.iib +// misc/inventar.sib +// room/test.rdi +// txt/diah.adh +// txt/inv_st.s and txt/room_st.s (inventory/room control bytes) +// txt/inv_use.idx + +Resource::Resource(Common::String filename) { + const uint32 headerGeneric = MKTAG('N', 'G', 'S', '\0'); + const uint32 headerTxtDec = MKTAG('T', 'C', 'F', '\0'); + const uint32 headerTxtEnc = MKTAG('T', 'C', 'F', '\1'); + const uint32 headerSprite = MKTAG('T', 'A', 'F', '\0'); + + filename.toLowercase(); + _stream.open(filename); + + uint32 header = _stream.readUint32BE(); + bool isText = (header == headerTxtDec || header == headerTxtEnc); + bool isSprite = (header == headerSprite); + + if (header != headerGeneric && !isSprite && !isText) + error("Invalid resource - %s", filename.c_str()); + + if (isText) { + _resType = kResourceTCF; + _encrypted = (header == headerTxtEnc); + } else if (isSprite) { + initSprite(filename); + return; + } else { + _resType = (ResourceType)_stream.readUint16LE(); + _encrypted = false; + } + + if (filename == "atds.tap") + _encrypted = true; + + _chunkCount = _stream.readUint16LE(); + + for (uint i = 0; i < _chunkCount; i++) { + Chunk cur; + cur.size = _stream.readUint32LE(); + + if (!isText) + cur.type = (ResourceType)_stream.readUint16LE(); + else + cur.num = _stream.readUint16LE(); + + cur.pos = _stream.pos(); + + _stream.skip(cur.size); + _chunkList.push_back(cur); + } +} + +Resource::~Resource() { + _chunkList.clear(); + _stream.close(); +} + +uint32 Resource::getChunkCount() const { + return _chunkList.size(); +} + +Chunk *Resource::getChunk(uint num) { + assert(num < _chunkList.size()); + + return &_chunkList[num]; +} + +byte *Resource::getChunkData(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + byte *data = new byte[chunk->size]; + + _stream.seek(chunk->pos, SEEK_SET); + _stream.read(data, chunk->size); + if (_encrypted) + decrypt(data, chunk->size); + + return data; +} + +void Resource::initSprite(Common::String filename) { + uint32 nextSpriteOffset; + + // TAF (sprite) resources are much different than the rest, so we have a + // separate initializer for them here + + _resType = kResourceTAF; + _encrypted = false; + /*screenMode = */_stream.readUint16LE(); + _chunkCount = _stream.readUint16LE(); + _stream.skip(4); // total size of all sprites + _stream.skip(3 * 256); // palette + nextSpriteOffset = _stream.readUint32LE(); + _stream.skip(2 + 1); // correction table, padding + if ((int32)nextSpriteOffset != _stream.pos()) + error("Invalid sprite resource - %s", filename.c_str()); + + for (uint i = 0; i < _chunkCount; i++) { + Chunk cur; + + cur.pos = _stream.pos(); + cur.type = kResourceTAF; + + _stream.skip(2 + 2 + 2); // compression flag, width, height + nextSpriteOffset = _stream.readUint32LE(); + uint32 spriteImageOffset = _stream.readUint32LE(); + _stream.skip(1); // padding + + if ((int32)spriteImageOffset != _stream.pos()) + error("Invalid sprite resource - %s", filename.c_str()); + + cur.size = nextSpriteOffset - cur.pos - 15; // 15 = sizeof(TAFChunk) + + _stream.skip(cur.size); + _chunkList.push_back(cur); + } +} + +void Resource::unpackRLE(byte *buffer, uint32 compressedSize, uint32 uncompressedSize) { + // Compressed images are packed using a very simple RLE compression + byte count; + byte value; + uint32 outPos = 0; + + for (uint i = 0; i < (compressedSize) / 2 && outPos < uncompressedSize; i++) { + count = _stream.readByte(); + value = _stream.readByte(); + for (byte j = 0; j < count; j++) { + buffer[outPos++] = value; + } + } +} + +void Resource::decrypt(byte *data, uint32 size) { + byte *c = data; + + for (uint i = 0; i < size; i++) { + *c = -(*c); + ++c; + } +} + +TAFChunk *SpriteResource::getSprite(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + TAFChunk *taf = new TAFChunk(); + + _stream.seek(chunk->pos, SEEK_SET); + + taf->compressionFlag = _stream.readUint16LE(); + taf->width = _stream.readUint16LE(); + taf->height = _stream.readUint16LE(); + _stream.skip(4 + 4 + 1); // nextSpriteOffset, spriteImageOffset, padding + + taf->data = new byte[taf->width * taf->height]; + + if (!taf->compressionFlag) + _stream.read(taf->data, chunk->size); + else + unpackRLE(taf->data, chunk->size, taf->width * taf->height); + + return taf; +} + +TBFChunk *BackgroundResource::getImage(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + TBFChunk *tbf = new TBFChunk(); + + _stream.seek(chunk->pos, SEEK_SET); + + if (_stream.readUint32BE() != MKTAG('T', 'B', 'F', '\0')) + error("Corrupt TBF resource"); + + tbf->screenMode = _stream.readUint16LE(); + tbf->compressionFlag = _stream.readUint16LE(); + tbf->size = _stream.readUint32LE(); + tbf->width = _stream.readUint16LE(); + tbf->height = _stream.readUint16LE(); + for (int j = 0; j < 3 * 256; j++) + tbf->palette[j] = (_stream.readByte() << 2) & 0xff; + + tbf->data = new byte[tbf->size]; + + if (!tbf->compressionFlag) + _stream.read(tbf->data, chunk->size); + else + unpackRLE(tbf->data, chunk->size, tbf->size); + + return tbf; +} + +SoundChunk *SoundResource::getSound(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + SoundChunk *sound = new SoundChunk(); + + _stream.seek(chunk->pos, SEEK_SET); + + // Voice files are split in blocks, so reassemble them here + byte blocksRemaining; + uint32 totalLength = 0; + uint32 blockSize; + + // Find the total length of the voice file + do { + blocksRemaining = _stream.readByte(); + + byte b1 = _stream.readByte(); + byte b2 = _stream.readByte(); + byte b3 = _stream.readByte(); + blockSize = b1 + (b2 << 8) + (b3 << 16); + + totalLength += blockSize; + _stream.skip(blockSize); + } while (blocksRemaining > 1); + + // Read the voice data + sound->size = totalLength; + sound->data = new byte[totalLength]; + byte *ptr = sound->data; + + _stream.seek(chunk->pos, SEEK_SET); + + do { + blocksRemaining = _stream.readByte(); + + byte b1 = _stream.readByte(); + byte b2 = _stream.readByte(); + byte b3 = _stream.readByte(); + blockSize = b1 + (b2 << 8) + (b3 << 16); + + _stream.read(ptr, blockSize); + ptr += blockSize; + } while (blocksRemaining > 1); + + return sound; +} + +VideoChunk *VideoResource::getVideoHeader(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + VideoChunk *vid = new VideoChunk(); + + _stream.seek(chunk->pos, SEEK_SET); + + if (_stream.readUint32BE() != MKTAG('C', 'F', 'O', '\0')) + error("Corrupt video resource"); + + vid->size = _stream.readUint32LE(); // always 0 + vid->frameCount = _stream.readUint16LE(); + vid->width = _stream.readUint16LE(); + vid->height = _stream.readUint16LE(); + vid->frameDelay = _stream.readUint32LE(); + vid->firstFrameOffset = _stream.readUint32LE(); // always 22 + + return vid; +} + +Common::SeekableReadStream *VideoResource::getVideoStream(uint num) { + assert(num < _chunkList.size()); + + Chunk *chunk = &_chunkList[num]; + return new Common::SeekableSubReadStream(&_stream, chunk->pos, chunk->pos + chunk->size); +} + +} // End of namespace Chewy |