From 5fccc0f98dda5cbcec208b17c2e6d36a36fadf1e Mon Sep 17 00:00:00 2001 From: Nicola Mettifogo Date: Fri, 12 Jun 2009 05:03:18 +0000 Subject: * Final version of the IFF parsing code. * Refactored ILBMDecoder usage from disk code. svn-id: r41458 --- engines/parallaction/disk.cpp | 121 +++++++++++++++++++++++ engines/parallaction/disk.h | 40 +++++++- engines/parallaction/disk_br.cpp | 113 +++++++++------------ engines/parallaction/disk_ns.cpp | 72 +++----------- engines/parallaction/gfxbase.cpp | 8 +- engines/parallaction/iff.cpp | 209 +++++++++++---------------------------- engines/parallaction/iff.h | 191 ++++++++++++++++++++++++++--------- engines/parallaction/module.mk | 1 + 8 files changed, 422 insertions(+), 333 deletions(-) create mode 100644 engines/parallaction/disk.cpp (limited to 'engines') diff --git a/engines/parallaction/disk.cpp b/engines/parallaction/disk.cpp new file mode 100644 index 0000000000..a58fa35655 --- /dev/null +++ b/engines/parallaction/disk.cpp @@ -0,0 +1,121 @@ +#include "parallaction/disk.h" +#include "parallaction/graphics.h" +#include "parallaction/iff.h" + +namespace Parallaction { + +void ILBMLoader::setupBuffer(uint32 w, uint32 h) { + _intBuffer = 0; + switch (_bodyMode) { + case BODYMODE_SURFACE: + if (!_surf) { + _surf = new Graphics::Surface; + assert(_surf); + } + _surf->create(w, h, 1); + _mode = ILBMDecoder::ILBM_UNPACK_PLANES; + _intBuffer = (byte*)_surf->pixels; + break; + + case BODYMODE_MASKBUFFER: + if (!_maskBuffer) { + _maskBuffer = new MaskBuffer; + assert(_maskBuffer); + } + _maskBuffer->create(w, h); + _mode = ILBMDecoder::ILBM_2_PACK_PLANES; + _intBuffer = _maskBuffer->data; + break; + + case BODYMODE_PATHBUFFER: + if (!_pathBuffer) { + _pathBuffer = new PathBuffer; + assert(_pathBuffer); + } + _pathBuffer->create(w, h); + _mode = ILBMDecoder::ILBM_1_PACK_PLANES; + _intBuffer = _pathBuffer->data; + break; + + default: + error("Invalid bodyMode '%i' for ILBMLoader", _bodyMode); + break; + } +} + +bool ILBMLoader::callback(IFFChunk &chunk) { + switch (chunk._type) { + case ID_BMHD: + _decoder.loadHeader(chunk._stream); + break; + + case ID_CMAP: + if (_palette) { + chunk._stream->read(_palette, chunk._size); + } + break; + + case ID_CRNG: + if (_crng) { + PaletteFxRange *ptr = &_crng[_numCRNG]; + chunk._stream->read((byte*)ptr, chunk._size); + ptr->_timer = FROM_BE_16(ptr->_timer); + ptr->_step = FROM_BE_16(ptr->_step); + ptr->_flags = FROM_BE_16(ptr->_flags); + ++_numCRNG; + } + break; + + case ID_BODY: + setupBuffer(_decoder._header.width, _decoder._header.height); + assert(_intBuffer); + _decoder.loadBitmap(_mode, _intBuffer, chunk._stream); + return true; // stop the parser + } + + return false; +} + +void ILBMLoader::load(Common::ReadStream *in, bool disposeStream) { + IFFParser parser(in, disposeStream); + Common::Functor1Mem< IFFChunk&, bool, ILBMLoader > c(this, &ILBMLoader::callback); + parser.parse(c); +} + +ILBMLoader::ILBMLoader(uint32 bodyMode, byte *palette, PaletteFxRange *crng) { + _bodyMode = bodyMode; + _surf = 0; + _maskBuffer = 0; + _pathBuffer = 0; + _palette = palette; + _crng = crng; + _numCRNG = 0; +} + +ILBMLoader::ILBMLoader(Graphics::Surface *surf, byte *palette, PaletteFxRange *crng) { + _bodyMode = ILBMLoader::BODYMODE_SURFACE; + _surf = surf; + _palette = palette; + _crng = crng; + _numCRNG = 0; +} + +ILBMLoader::ILBMLoader(MaskBuffer *buffer) { + _bodyMode = ILBMLoader::BODYMODE_MASKBUFFER; + _maskBuffer = buffer; + _palette = 0; + _crng = 0; + _numCRNG = 0; +} + +ILBMLoader::ILBMLoader(PathBuffer *buffer) { + _bodyMode = ILBMLoader::BODYMODE_PATHBUFFER; + _pathBuffer = buffer; + _palette = 0; + _crng = 0; + _numCRNG = 0; +} + + + +} diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h index 4cc2711e96..936be1e140 100644 --- a/engines/parallaction/disk.h +++ b/engines/parallaction/disk.h @@ -33,6 +33,7 @@ #include "common/file.h" #include "graphics/surface.h" +#include "parallaction/iff.h" @@ -77,8 +78,37 @@ public: virtual Table* loadTable(const char* name) = 0; virtual Common::SeekableReadStream* loadMusic(const char* name) = 0; virtual Common::SeekableReadStream* loadSound(const char* name) = 0; - virtual void loadMask(const char *name, MaskBuffer &buffer) { } - virtual void loadPath(const char *name, PathBuffer &buffer) { } + virtual MaskBuffer *loadMask(const char *name, uint32 w, uint32 h) { return 0; } + virtual PathBuffer *loadPath(const char *name, uint32 w, uint32 h) { return 0; } +}; + +struct PaletteFxRange; + +struct ILBMLoader { + enum { + BODYMODE_SURFACE, + BODYMODE_MASKBUFFER, + BODYMODE_PATHBUFFER + }; + uint32 _bodyMode; + Graphics::Surface *_surf; + MaskBuffer *_maskBuffer; + PathBuffer *_pathBuffer; + byte *_palette; + PaletteFxRange *_crng; + uint32 _mode; + byte* _intBuffer; + uint32 _numCRNG; + ILBMDecoder _decoder; + + ILBMLoader(uint32 bodyMode, byte *palette = 0, PaletteFxRange *crng = 0); + ILBMLoader(Graphics::Surface *surf, byte *palette = 0, PaletteFxRange *crng = 0); + ILBMLoader(MaskBuffer *buffer); + ILBMLoader(PathBuffer *buffer); + + bool callback(IFFChunk &chunk); + void setupBuffer(uint32 w, uint32 h); + void load(Common::ReadStream *in, bool disposeStream = false); }; @@ -235,8 +265,8 @@ public: Table* loadTable(const char* name); Common::SeekableReadStream* loadMusic(const char* name); Common::SeekableReadStream* loadSound(const char* name); - void loadMask(const char *name, MaskBuffer &buffer); - void loadPath(const char *name, PathBuffer &buffer); + MaskBuffer *loadMask(const char *name, uint32 w, uint32 h); + PathBuffer *loadPath(const char *name, uint32 w, uint32 h); }; class DosDemoDisk_br : public DosDisk_br { @@ -272,7 +302,7 @@ public: GfxObj* loadObjects(const char *name, uint8 part = 0); Common::SeekableReadStream* loadMusic(const char* name); Common::SeekableReadStream* loadSound(const char* name); - void loadMask(const char *name, MaskBuffer &buffer); + MaskBuffer *loadMask(const char *name, uint32 w, uint32 h); }; } // namespace Parallaction diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index ec4fc32cc1..3572129dc0 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -331,32 +331,40 @@ void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) { } } -void DosDisk_br::loadMask(const char *name, MaskBuffer &buffer) { +MaskBuffer *DosDisk_br::loadMask(const char *name, uint32 w, uint32 h) { if (!name) { - return; + return 0; } Common::SeekableReadStream *stream = openFile("msk/" + Common::String(name), ".msk"); - // NOTE: info.width and info.height are only valid if the background graphics - // have already been loaded - buffer.bigEndian = false; - stream->read(buffer.data, buffer.size); + MaskBuffer *buffer = new MaskBuffer; + assert(buffer); + buffer->create(w, h); + buffer->bigEndian = false; + + stream->read(buffer->data, buffer->size); delete stream; + + return buffer; } -void DosDisk_br::loadPath(const char *name, PathBuffer &buffer) { +PathBuffer *DosDisk_br::loadPath(const char *name, uint32 w, uint32 h) { if (!name) { - return; + return 0; } Common::SeekableReadStream *stream = openFile("pth/" + Common::String(name), ".pth"); - // NOTE: info.width and info.height are only valid if the background graphics - // have already been loaded - buffer.bigEndian = false; - stream->read(buffer.data, buffer.size); + PathBuffer *buffer = new PathBuffer; + assert(buffer); + buffer->create(w, h); + buffer->bigEndian = false; + + stream->read(buffer->data, buffer->size); delete stream; + + return buffer; } void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) { @@ -380,18 +388,12 @@ void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char } if (mask) { - info._mask = new MaskBuffer; - info._mask->create(info.width, info.height); - loadMask(mask, *info._mask); + info._mask = loadMask(mask, info.width, info.height); } if (path) { - info._path = new PathBuffer; - info._path->create(info.width, info.height); - loadPath(path, *info._path); + info._path = loadPath(path, info.width, info.height); } - - return; } Table* DosDisk_br::loadTable(const char* name) { @@ -459,7 +461,7 @@ void AmigaDisk_br::adjustForPalette(Graphics::Surface &surf, int transparentColo void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { byte r,g,b; - byte *pal, *p; + byte *p; Common::SeekableReadStream *stream; uint i; @@ -488,20 +490,14 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { } stream = openFile("backs/" + Common::String(filename), ".bkg"); - ILBMDecoder decoder(stream, true); - // TODO: encapsulate surface creation - info.bg.w = decoder.getWidth(); - info.bg.h = decoder.getHeight(); - info.bg.pitch = info.bg.w; - info.bg.bytesPerPixel = 1; - info.bg.pixels = decoder.getBitmap(); - assert(info.bg.pixels); + byte pal[768]; + ILBMLoader loader(&info.bg, pal); + loader.load(stream, true); info.width = info.bg.w; info.height = info.bg.h; - pal = decoder.getPalette(); p = pal; for (i = 16; i < 32; i++) { r = *p >> 2; @@ -516,8 +512,6 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { // Overwrite the first color (transparent key) in the palette info.palette.setEntry(0, pal[0] >> 2, pal[1] >> 2, pal[2] >> 0); - delete []pal; - // background data is drawn used the upper portion of the palette adjustForPalette(info.bg); } @@ -543,27 +537,24 @@ void finalpass(byte *buffer, uint32 size) { } } -void AmigaDisk_br::loadMask(const char *name, MaskBuffer &buffer) { +MaskBuffer *AmigaDisk_br::loadMask(const char *name, uint32 w, uint32 h) { if (!name) { - return; + return 0; } debugC(1, kDebugDisk, "AmigaDisk_br::loadMask '%s'", name); Common::SeekableReadStream *stream = tryOpenFile("msk/" + Common::String(name), ".msk"); if (!stream) { - return; + return 0; } - ILBMDecoder decoder(stream, true); + ILBMLoader loader(ILBMLoader::BODYMODE_MASKBUFFER); + loader.load(stream, true); - // TODO: the buffer is allocated by the caller, so a copy here is - // unavoidable... a better solution would be inform the function - // of the size of the mask (the size in the mask file is not valid!) - byte *bitmap = decoder.getBitmap(2, true); - memcpy(buffer.data, bitmap, buffer.size); - finalpass(buffer.data, buffer.size); - - buffer.bigEndian = true; + MaskBuffer *buffer = loader._maskBuffer; + buffer->bigEndian = true; + finalpass(buffer->data, buffer->size); + return buffer; } void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) { @@ -573,18 +564,12 @@ void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const cha loadBackground(info, name); } if (mask) { - info._mask = new MaskBuffer; - info._mask->create(info.width, info.height); - loadMask(mask, *info._mask); + info._mask = loadMask(mask, info.width, info.height); } if (path) { - info._path = new PathBuffer; - info._path->create(info.width, info.height); - loadPath(path, *info._path); + info._path = loadPath(path, info.width, info.height); } - - return; } void AmigaDisk_br::loadSlide(BackgroundInfo& info, const char *name) { @@ -596,20 +581,13 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) { debugC(1, kDebugDisk, "AmigaDisk_br::loadStatic '%s'", name); Common::String sName = name; - Common::SeekableReadStream *stream = openFile("ras/" + sName, ".ras"); - ILBMDecoder decoder(stream, true); - Graphics::Surface* surf = new Graphics::Surface; - assert(surf); + ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE); + loader.load(stream, true); - // TODO: encapsulate surface creation - surf->w = decoder.getWidth(); - surf->h = decoder.getHeight(); - surf->pitch = surf->w; - surf->bytesPerPixel = 1; - surf->pixels = decoder.getBitmap(); - assert(surf->pixels); + Graphics::Surface* surf = loader._surf; + assert(surf); // Static pictures are drawn used the upper half of the palette: this must be // done before shadow mask is applied. This way, only really transparent pixels @@ -741,15 +719,16 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) { debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects"); Common::SeekableReadStream *stream = openFile(name); - ILBMDecoder decoder(stream, true); + ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE); + loader.load(stream, true); uint16 max = objectsMax[part]; if (_vm->getFeatures() & GF_DEMO) max = 72; byte *data = new byte[max * 2601]; - byte *srcPtr = decoder.getBitmap(); - int w = decoder.getWidth(); + byte *srcPtr = (byte*)loader._surf->getBasePtr(0,0); + int w = loader._surf->w; // Convert to the expected display format for (int i = 0; i < max; i++) { @@ -764,7 +743,7 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) { dst += 51; } } - free(srcPtr); + delete loader._surf; return new GfxObj(0, new Cnv(max, 51, 51, data, true)); } diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 8aa2a9f543..a75decdf88 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -900,56 +900,18 @@ void AmigaDisk_ns::buildMask(byte* buf) { } } -// TODO: extend the ILBMDecoder to return CRNG chunks and get rid of this BackgroundDecoder crap -class BackgroundDecoder : public ILBMDecoder { - -public: - BackgroundDecoder(Common::SeekableReadStream *input, bool disposeStream = false) : ILBMDecoder(input, disposeStream) { - } - - uint32 getCRNG(PaletteFxRange *ranges, uint32 num) { - assert(ranges); - - uint32 size = _parser.getIFFBlockSize(ID_CRNG); - if (size == (uint32)-1) { - return 0; - } - - uint32 count = MIN((uint32)(size / sizeof(PaletteFxRange)), num); - _parser.loadIFFBlock(ID_CRNG, ranges, count * sizeof(PaletteFxRange)); - - for (uint32 i = 0; i < count; ++i) { - ranges[i]._timer = FROM_BE_16(ranges[i]._timer); - ranges[i]._step = FROM_BE_16(ranges[i]._step); - ranges[i]._flags = FROM_BE_16(ranges[i]._flags); - } - - return count; - } -}; - void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) { - - Common::SeekableReadStream *s = openFile(name); - BackgroundDecoder decoder(s, true); - PaletteFxRange ranges[6]; - memset(ranges, 0, 6*sizeof(PaletteFxRange)); - decoder.getCRNG(ranges, 6); + byte pal[768]; - // TODO: encapsulate surface creation - info.bg.w = decoder.getWidth(); - info.bg.h = decoder.getHeight(); - info.bg.pitch = info.bg.w; - info.bg.bytesPerPixel = 1; - info.bg.pixels = decoder.getBitmap(); + Common::SeekableReadStream *s = openFile(name); + ILBMLoader loader(&info.bg, pal, ranges); + loader.load(s, true); info.width = info.bg.w; info.height = info.bg.h; - byte *pal = decoder.getPalette(); - assert(pal); byte *p = pal; for (uint i = 0; i < 32; i++) { byte r = *p >> 2; @@ -960,7 +922,6 @@ void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) { p++; info.palette.setEntry(i, r, g, b); } - delete []pal; for (uint j = 0; j < 6; j++) { info.setPaletteRange(j, ranges[j]); @@ -979,9 +940,9 @@ void AmigaDisk_ns::loadMask(BackgroundInfo& info, const char *name) { return; // no errors if missing mask files: not every location has one } - ILBMDecoder decoder(s, true); - byte *pal = decoder.getPalette(); - assert(pal); + byte pal[768]; + ILBMLoader loader(ILBMLoader::BODYMODE_MASKBUFFER, pal); + loader.load(s, true); byte r, g, b; for (uint i = 0; i < 4; i++) { @@ -990,14 +951,8 @@ void AmigaDisk_ns::loadMask(BackgroundInfo& info, const char *name) { b = pal[i*3+2]; info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF; } - delete []pal; - info._mask = new MaskBuffer; - info._mask->w = info.width; - info._mask->h = info.height; - info._mask->internalWidth = info.width >> 2; - info._mask->size = info._mask->internalWidth * info._mask->h; - info._mask->data = decoder.getBitmap(2, true); + info._mask = loader._maskBuffer; } void AmigaDisk_ns::loadPath(BackgroundInfo& info, const char *name) { @@ -1010,15 +965,10 @@ void AmigaDisk_ns::loadPath(BackgroundInfo& info, const char *name) { return; // no errors if missing path files: not every location has one } - ILBMDecoder decoder(s, true); - info._path = new PathBuffer; - info._path->create(info.width, info.height); + ILBMLoader loader(ILBMLoader::BODYMODE_PATHBUFFER); + loader.load(s, true); + info._path = loader._pathBuffer; info._path->bigEndian = true; - - byte *bitmap = decoder.getBitmap(1, true); - assert(bitmap); - memcpy(info._path->data, bitmap, info._path->size); - delete bitmap; } void AmigaDisk_ns::loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) { diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index ec72b14c15..fc6cb28d9e 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -162,9 +162,7 @@ void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) { Common::Rect rect; obj->getRect(0, rect); - MaskBuffer *buf = new MaskBuffer; - buf->create(rect.width(), rect.height()); - _vm->_disk->loadMask(name, *buf); + MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height()); obj->_maskId = addMaskPatch(buf); obj->_hasMask = true; @@ -174,9 +172,7 @@ void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) { Common::Rect rect; obj->getRect(0, rect); - PathBuffer *buf = new PathBuffer; - buf->create(rect.width(), rect.height()); - _vm->_disk->loadPath(name, *buf); + PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height()); obj->_pathId = addPathPatch(buf); obj->_hasPath = true; diff --git a/engines/parallaction/iff.cpp b/engines/parallaction/iff.cpp index 43dcac3697..51e1e1d186 100644 --- a/engines/parallaction/iff.cpp +++ b/engines/parallaction/iff.cpp @@ -32,34 +32,17 @@ namespace Parallaction { -void IFFParser::setInputStream(Common::SeekableReadStream *stream) { - destroy(); - +void IFFParser::setInputStream(Common::ReadStream *stream) { assert(stream); - _stream = stream; - _startOffset = 0; - _endOffset = _stream->size(); - - _formType = 0; - _formSize = (uint32)-1; - - if (_stream->size() < 12) { - // this file is too small to be a valid IFF container - return; - } + _formChunk.setInputStream(stream); + _chunk.setInputStream(stream); - if (_stream->readUint32BE() != ID_FORM) { - // no FORM header was found - return; + _formChunk.readHeader(); + if (_formChunk.id != ID_FORM) { + error("IFFParser input is not a FORM type IFF file"); } - - _formSize = _stream->readUint32BE(); - _formType = _stream->readUint32BE(); -} - -void IFFParser::destroy() { - _stream = 0; - _startOffset = _endOffset = 0; + _formSize = _formChunk.size; + _formType = _formChunk.readUint32BE(); } uint32 IFFParser::getFORMSize() const { @@ -70,171 +53,99 @@ Common::IFF_ID IFFParser::getFORMType() const { return _formType; } -uint32 IFFParser::moveToIFFBlock(Common::IFF_ID chunkName) { - uint32 size = (uint32)-1; - - _stream->seek(_startOffset + 0x0C); - - while ((uint)_stream->pos() < _endOffset) { - uint32 chunk = _stream->readUint32BE(); - uint32 size_temp = _stream->readUint32BE(); +void IFFParser::parse(IFFCallback &callback) { + bool stop; + do { + _chunk.feed(); + _formChunk.incBytesRead(_chunk.size); - if (chunk != chunkName) { - _stream->seek((size_temp + 1) & (~1), SEEK_CUR); - assert((uint)_stream->pos() <= _endOffset); - } else { - size = size_temp; + if (_formChunk.hasReadAll()) { break; } - } - - return size; -} - -uint32 IFFParser::getIFFBlockSize(Common::IFF_ID chunkName) { - uint32 size = moveToIFFBlock(chunkName); - return size; -} - -bool IFFParser::loadIFFBlock(Common::IFF_ID chunkName, void *loadTo, uint32 ptrSize) { - uint32 chunkSize = moveToIFFBlock(chunkName); - - if (chunkSize == (uint32)-1) { - return false; - } - - uint32 loadSize = 0; - loadSize = MIN(ptrSize, chunkSize); - _stream->read(loadTo, loadSize); - return true; -} - -Common::SeekableReadStream *IFFParser::getIFFBlockStream(Common::IFF_ID chunkName) { - uint32 chunkSize = moveToIFFBlock(chunkName); - - if (chunkSize == (uint32)-1) { - return 0; - } - - uint32 pos = _stream->pos(); - return new Common::SeekableSubReadStream(_stream, pos, pos + chunkSize, false); -} + _formChunk.incBytesRead(8); + _chunk.readHeader(); -// ILBM decoder implementation + // invoke the callback + Common::SubReadStream stream(&_chunk, _chunk.size); + IFFChunk chunk(_chunk.id, _chunk.size, &stream); + stop = callback(chunk); -ILBMDecoder::ILBMDecoder(Common::SeekableReadStream *in, bool disposeStream) : _in(in), _disposeStream(disposeStream), _hasHeader(false), _bodySize((uint32)-1), _paletteSize((uint32)-1) { - assert(in); - _parser.setInputStream(in); - - if (_parser.getFORMType() != ID_ILBM) { - return; - } - - _hasHeader = _parser.loadIFFBlock(ID_BMHD, &_header, sizeof(_header)); - if (!_hasHeader) { - return; - } - - _header.width = TO_BE_16(_header.width); - _header.height = TO_BE_16(_header.height); - - _paletteSize = _parser.getIFFBlockSize(ID_CMAP); - _bodySize = _parser.getIFFBlockSize(ID_BODY); -} + // eats up all the remaining data in the chunk + while (!stream.eos()) { + printf("attemping to eat data in chunk\n"); + stream.readByte(); + } -ILBMDecoder::~ILBMDecoder() { - if (_disposeStream) { - delete _in; - } + } while (!stop); } -uint32 ILBMDecoder::getWidth() { - assert(_hasHeader); - return _header.width; -} -uint32 ILBMDecoder::getHeight() { - assert(_hasHeader); - return _header.height; -} -uint32 ILBMDecoder::getNumColors() { - assert(_hasHeader); - return (1 << _header.depth); -} - -byte *ILBMDecoder::getPalette() { - assert(_paletteSize != (uint32)-1); - byte *palette = new byte[_paletteSize]; - assert(palette); - _parser.loadIFFBlock(ID_CMAP, palette, _paletteSize); - return palette; +void ILBMDecoder::loadHeader(Common::ReadStream *stream) { + assert(stream); + stream->read(&_header, sizeof(_header)); + _header.width = FROM_BE_16(_header.width); + _header.height = FROM_BE_16(_header.height); + _header.x = FROM_BE_16(_header.x); + _header.y = FROM_BE_16(_header.y); + _header.transparentColor = FROM_BE_16(_header.transparentColor); + _header.pageWidth = FROM_BE_16(_header.pageWidth); + _header.pageHeight = FROM_BE_16(_header.pageHeight); } -byte *ILBMDecoder::getBitmap(uint32 numPlanes, bool packPlanes) { - assert(_bodySize != (uint32)-1); +void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream) { + assert(stream); + uint32 numPlanes = MIN(mode & ILBM_UNPACK_PLANES, (uint32)_header.depth); assert(numPlanes == 1 || numPlanes == 2 || numPlanes == 3 || numPlanes == 4 || numPlanes == 5 || numPlanes == 8); - numPlanes = MIN(numPlanes, (uint32)_header.depth); - if (numPlanes > 4) { - packPlanes = false; + bool packPixels = (mode & ILBM_PACK_PLANES) != 0; + if (numPlanes != 1 && numPlanes != 2 && numPlanes != 4) { + packPixels = false; } - uint32 bitmapSize = _header.width * _header.height; - uint32 bitmapWidth = _header.width; - if (packPlanes) { - bitmapSize /= (8 / numPlanes); - bitmapWidth /= (8 / numPlanes); + uint32 outPitch = _header.width; + if (packPixels) { + outPitch /= (8 / numPlanes); } - - Common::SeekableReadStream *bodyStream = _parser.getIFFBlockStream(ID_BODY); - assert(bodyStream); - - byte *bitmap = (byte*)calloc(bitmapSize, 1); - assert(bitmap); + byte *out = buffer; switch (_header.pack) { case 1: { // PackBits compressed bitmap - Graphics::PackBitsReadStream stream(*bodyStream); - - byte *out = bitmap; + Graphics::PackBitsReadStream packStream(*stream); // setup a buffer to hold enough data to build a line in the output - uint32 scanWidth = ((_header.width + 15)/16) << 1; - byte *scanBuffer = (byte*)malloc(scanWidth * _header.depth); + uint32 scanlineWidth = ((_header.width + 15)/16) << 1; + byte *scanline = new byte[scanlineWidth * _header.depth]; for (uint i = 0; i < _header.height; ++i) { - byte *s = scanBuffer; + byte *s = scanline; for (uint32 j = 0; j < _header.depth; ++j) { - stream.read(s, scanWidth); - s += scanWidth; + packStream.read(s, scanlineWidth); + s += scanlineWidth; } - planarToChunky(out, bitmapWidth, scanBuffer, scanWidth, numPlanes, packPlanes); - out += bitmapWidth; + planarToChunky(out, outPitch, scanline, scanlineWidth, numPlanes, packPixels); + out += outPitch; } - free(scanBuffer); + delete []scanline; break; } + default: + // implement other compression types here! error("only RLE compressed ILBM files are supported"); break; } - - delete bodyStream; - - return bitmap; } - -void ILBMDecoder::planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes) { +void ILBMDecoder::planarToChunky(byte *out, uint32 outPitch, byte *in, uint32 inWidth, uint32 nPlanes, bool packPlanes) { byte pix, ofs, bit; byte *s; - uint32 pixels = width; + uint32 pixels = outPitch; if (packPlanes) { pixels *= (8 / nPlanes); } @@ -251,7 +162,7 @@ void ILBMDecoder::planarToChunky(byte *out, uint32 width, byte *in, uint32 plane if (s[ofs] & bit) { pix |= (1 << plane); } - s += planeWidth; + s += inWidth; } diff --git a/engines/parallaction/iff.h b/engines/parallaction/iff.h index 43f78bf001..98e36e1b00 100644 --- a/engines/parallaction/iff.h +++ b/engines/parallaction/iff.h @@ -27,39 +27,126 @@ #define PARALLACTION_IFF_H #include "common/stream.h" +#include "common/func.h" #include "common/iff_container.h" // for IFF chunk names #include "graphics/iff.h" // for BMHD -// this IFF parser code is courtesy of the Kyra engine team ;) namespace Parallaction { +/** + * Represents a IFF chunk available to client code. + * + * Client code must *not* deallocate _stream when done. + */ +struct IFFChunk { + Common::IFF_ID _type; + uint32 _size; + Common::ReadStream *_stream; + + IFFChunk(Common::IFF_ID type, uint32 size, Common::ReadStream *stream) : _type(type), _size(size), _stream(stream) { + assert(_stream); + } +}; + +/** + * Parser for IFF containers. + */ class IFFParser { + + /** + * This private class implements IFF chunk navigation. + */ + class IFFChunkNav : public Common::ReadStream { + protected: + Common::ReadStream *_input; + uint32 _bytesRead; + public: + Common::IFF_ID id; + uint32 size; + + IFFChunkNav() : _input(0) { + } + void setInputStream(Common::ReadStream *input) { + _input = input; + size = _bytesRead = 0; + } + void incBytesRead(uint32 inc) { + _bytesRead += inc; + if (_bytesRead > size) { + error("Chunk overread"); + } + } + void readHeader() { + id = _input->readUint32BE(); + size = _input->readUint32BE(); + _bytesRead = 0; + } + bool hasReadAll() const { + return (size - _bytesRead) == 0; + } + void feed() { + if (size % 2) { + size++; + } + while (!hasReadAll()) { + readByte(); + } + } + // Common::ReadStream implementation + bool eos() const { return _input->eos(); } + bool err() const { return _input->err(); } + void clearErr() { _input->clearErr(); } + + uint32 read(void *dataPtr, uint32 dataSize) { + incBytesRead(dataSize); + return _input->read(dataPtr, dataSize); + } + }; + + IFFChunkNav _formChunk; //!< The root chunk of the file. + IFFChunkNav _chunk; //!< The current chunk. + + Common::ReadStream *_stream; + bool _disposeStream; + + void setInputStream(Common::ReadStream *stream); + public: - IFFParser() : _stream(0), _startOffset(0), _endOffset(0) {} - IFFParser(Common::SeekableReadStream *stream) : _stream(0), _startOffset(0), _endOffset(0) { + IFFParser(Common::ReadStream *stream, bool disposeStream = false) : _stream(stream), _disposeStream(stream) { setInputStream(stream); } - ~IFFParser() { destroy(); } - - void setInputStream(Common::SeekableReadStream *stream); + ~IFFParser() { + if (_disposeStream) { + delete _stream; + } + _stream = 0; + } - operator bool() const { return (_startOffset != _endOffset) && _stream; } + /** + * Returns the IFF FORM type. + * @return the IFF FORM type of the stream, or 0 if FORM header is not found. + */ + Common::IFF_ID getFORMType() const; + /** + * Returns the size of the data. + * @return the size of the data in file, or -1 if FORM header is not found. + */ uint32 getFORMSize() const; - Common::IFF_ID getFORMType() const; - uint32 getIFFBlockSize(Common::IFF_ID chunk); - bool loadIFFBlock(Common::IFF_ID chunk, void *loadTo, uint32 ptrSize); - Common::SeekableReadStream *getIFFBlockStream(Common::IFF_ID chunkName); -private: - void destroy(); - uint32 moveToIFFBlock(Common::IFF_ID chunkName); + /** + * Callback type for the parser. + */ + typedef Common::Functor1< IFFChunk&, bool > IFFCallback; - Common::SeekableReadStream *_stream; - uint32 _startOffset; - uint32 _endOffset; + /** + * Parse the IFF container, invoking the callback on each chunk encountered. + * The callback can interrupt the parsing by returning 'true'. + */ + void parse(IFFCallback &callback); +private: uint32 _formSize; Common::IFF_ID _formType; }; @@ -67,35 +154,49 @@ private: -class ILBMDecoder { - Common::SeekableReadStream *_in; - bool _disposeStream; - +struct ILBMDecoder { + /** + * ILBM header data, necessary for loadBitmap() + */ + Graphics::BMHD _header; + + /** + * Available decoding modes for loadBitmap(). + */ + enum { + ILBM_UNPACK_PLANES = 0xFF, //!< Decode all bitplanes, and map 1 pixel to 1 byte. + ILBM_PACK_PLANES = 0x100, //!< Request unpacking, used as a mask with below options. + + ILBM_1_PLANES = 1, //!< Decode only the first bitplane, don't pack. + ILBM_1_PACK_PLANES = ILBM_1_PLANES | ILBM_PACK_PLANES, //!< Decode only the first bitplane, pack 8 pixels in 1 byte. + ILBM_2_PLANES = 2, //!< Decode first 2 bitplanes, don't pack. + ILBM_2_PACK_PLANES = ILBM_2_PLANES | ILBM_PACK_PLANES, //!< Decode first 2 bitplanes, pack 4 pixels in 1 byte. + ILBM_3_PLANES = 3, //!< Decode first 3 bitplanes, don't pack. + ILBM_4_PLANES = 4, //!< Decode first 4 bitplanes, don't pack. + ILBM_4_PACK_PLANES = ILBM_4_PLANES | ILBM_PACK_PLANES, //!< Decode first 4 bitplanes, pack 2 pixels in 1 byte. + ILBM_5_PLANES = 5, //!< Decode first 5 bitplanes, don't pack. + ILBM_8_PLANES = 8 //!< Decode all 8 bitplanes. + }; + + /** + * Fills the _header member from the given stream. + */ + void loadHeader(Common::ReadStream *stream); + + /** + * Loads and unpacks the ILBM bitmap data from the stream into the buffer. + * The functions assumes the buffer is large enough to contain all data. + * The caller controls how data should be packed by choosing mode from + * the enum above. + */ + void loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream); + + /** + * Converts from bitplanar to chunky representation. Intended for internal + * usage, but you can be (ab)use it from client code if you know what you + * are doing. + */ void planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes); - -protected: - IFFParser _parser; - Graphics::BMHD _header; - bool _hasHeader; - uint32 _bodySize; - uint32 _paletteSize; - - -public: - ILBMDecoder(Common::SeekableReadStream *input, bool disposeStream = false); - - virtual ~ILBMDecoder(); - - uint32 getWidth(); - uint32 getHeight(); - uint32 getNumColors(); - byte *getPalette(); - - byte *getBitmap(uint32 numPlanes, bool packPlanes); - byte *getBitmap() { - assert(_hasHeader); - return getBitmap(_header.depth, false); - } }; diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index 16b79c3d5a..bd45598d17 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ debug.o \ detection.o \ dialogue.o \ + disk.o \ disk_br.o \ disk_ns.o \ exec.o \ -- cgit v1.2.3