* Final version of the IFF parsing code.

* Refactored ILBMDecoder usage from disk code.

svn-id: r41458
This commit is contained in:
Nicola Mettifogo 2009-06-12 05:03:18 +00:00
parent f5b2e69522
commit 5fccc0f98d
8 changed files with 423 additions and 334 deletions

View file

@ -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;
}
}

View file

@ -33,6 +33,7 @@
#include "common/file.h" #include "common/file.h"
#include "graphics/surface.h" #include "graphics/surface.h"
#include "parallaction/iff.h"
@ -77,8 +78,37 @@ public:
virtual Table* loadTable(const char* name) = 0; virtual Table* loadTable(const char* name) = 0;
virtual Common::SeekableReadStream* loadMusic(const char* name) = 0; virtual Common::SeekableReadStream* loadMusic(const char* name) = 0;
virtual Common::SeekableReadStream* loadSound(const char* name) = 0; virtual Common::SeekableReadStream* loadSound(const char* name) = 0;
virtual void loadMask(const char *name, MaskBuffer &buffer) { } virtual MaskBuffer *loadMask(const char *name, uint32 w, uint32 h) { return 0; }
virtual void loadPath(const char *name, PathBuffer &buffer) { } 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); Table* loadTable(const char* name);
Common::SeekableReadStream* loadMusic(const char* name); Common::SeekableReadStream* loadMusic(const char* name);
Common::SeekableReadStream* loadSound(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);
void loadPath(const char *name, PathBuffer &buffer); PathBuffer *loadPath(const char *name, uint32 w, uint32 h);
}; };
class DosDemoDisk_br : public DosDisk_br { class DosDemoDisk_br : public DosDisk_br {
@ -272,7 +302,7 @@ public:
GfxObj* loadObjects(const char *name, uint8 part = 0); GfxObj* loadObjects(const char *name, uint8 part = 0);
Common::SeekableReadStream* loadMusic(const char* name); Common::SeekableReadStream* loadMusic(const char* name);
Common::SeekableReadStream* loadSound(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 } // namespace Parallaction

View file

@ -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) { if (!name) {
return; return 0;
} }
Common::SeekableReadStream *stream = openFile("msk/" + Common::String(name), ".msk"); Common::SeekableReadStream *stream = openFile("msk/" + Common::String(name), ".msk");
// NOTE: info.width and info.height are only valid if the background graphics MaskBuffer *buffer = new MaskBuffer;
// have already been loaded assert(buffer);
buffer.bigEndian = false; buffer->create(w, h);
stream->read(buffer.data, buffer.size); buffer->bigEndian = false;
stream->read(buffer->data, buffer->size);
delete stream; 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) { if (!name) {
return; return 0;
} }
Common::SeekableReadStream *stream = openFile("pth/" + Common::String(name), ".pth"); Common::SeekableReadStream *stream = openFile("pth/" + Common::String(name), ".pth");
// NOTE: info.width and info.height are only valid if the background graphics PathBuffer *buffer = new PathBuffer;
// have already been loaded assert(buffer);
buffer.bigEndian = false; buffer->create(w, h);
stream->read(buffer.data, buffer.size); buffer->bigEndian = false;
stream->read(buffer->data, buffer->size);
delete stream; delete stream;
return buffer;
} }
void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) { 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) { if (mask) {
info._mask = new MaskBuffer; info._mask = loadMask(mask, info.width, info.height);
info._mask->create(info.width, info.height);
loadMask(mask, *info._mask);
} }
if (path) { if (path) {
info._path = new PathBuffer; info._path = loadPath(path, info.width, info.height);
info._path->create(info.width, info.height);
loadPath(path, *info._path);
} }
return;
} }
Table* DosDisk_br::loadTable(const char* name) { 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) { void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) {
byte r,g,b; byte r,g,b;
byte *pal, *p; byte *p;
Common::SeekableReadStream *stream; Common::SeekableReadStream *stream;
uint i; uint i;
@ -488,20 +490,14 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) {
} }
stream = openFile("backs/" + Common::String(filename), ".bkg"); stream = openFile("backs/" + Common::String(filename), ".bkg");
ILBMDecoder decoder(stream, true);
// TODO: encapsulate surface creation byte pal[768];
info.bg.w = decoder.getWidth(); ILBMLoader loader(&info.bg, pal);
info.bg.h = decoder.getHeight(); loader.load(stream, true);
info.bg.pitch = info.bg.w;
info.bg.bytesPerPixel = 1;
info.bg.pixels = decoder.getBitmap();
assert(info.bg.pixels);
info.width = info.bg.w; info.width = info.bg.w;
info.height = info.bg.h; info.height = info.bg.h;
pal = decoder.getPalette();
p = pal; p = pal;
for (i = 16; i < 32; i++) { for (i = 16; i < 32; i++) {
r = *p >> 2; 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 // Overwrite the first color (transparent key) in the palette
info.palette.setEntry(0, pal[0] >> 2, pal[1] >> 2, pal[2] >> 0); 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 // background data is drawn used the upper portion of the palette
adjustForPalette(info.bg); 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) { if (!name) {
return; return 0;
} }
debugC(1, kDebugDisk, "AmigaDisk_br::loadMask '%s'", name); debugC(1, kDebugDisk, "AmigaDisk_br::loadMask '%s'", name);
Common::SeekableReadStream *stream = tryOpenFile("msk/" + Common::String(name), ".msk"); Common::SeekableReadStream *stream = tryOpenFile("msk/" + Common::String(name), ".msk");
if (!stream) { 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 MaskBuffer *buffer = loader._maskBuffer;
// unavoidable... a better solution would be inform the function buffer->bigEndian = true;
// of the size of the mask (the size in the mask file is not valid!) finalpass(buffer->data, buffer->size);
byte *bitmap = decoder.getBitmap(2, true); return buffer;
memcpy(buffer.data, bitmap, buffer.size);
finalpass(buffer.data, buffer.size);
buffer.bigEndian = true;
} }
void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) { 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); loadBackground(info, name);
} }
if (mask) { if (mask) {
info._mask = new MaskBuffer; info._mask = loadMask(mask, info.width, info.height);
info._mask->create(info.width, info.height);
loadMask(mask, *info._mask);
} }
if (path) { if (path) {
info._path = new PathBuffer; info._path = loadPath(path, info.width, info.height);
info._path->create(info.width, info.height);
loadPath(path, *info._path);
} }
return;
} }
void AmigaDisk_br::loadSlide(BackgroundInfo& info, const char *name) { void AmigaDisk_br::loadSlide(BackgroundInfo& info, const char *name) {
@ -596,21 +581,14 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_br::loadStatic '%s'", name); debugC(1, kDebugDisk, "AmigaDisk_br::loadStatic '%s'", name);
Common::String sName = name; Common::String sName = name;
Common::SeekableReadStream *stream = openFile("ras/" + sName, ".ras"); Common::SeekableReadStream *stream = openFile("ras/" + sName, ".ras");
ILBMDecoder decoder(stream, true);
Graphics::Surface* surf = new Graphics::Surface; ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE);
loader.load(stream, true);
Graphics::Surface* surf = loader._surf;
assert(surf); assert(surf);
// 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);
// Static pictures are drawn used the upper half of the palette: this must be // 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 // done before shadow mask is applied. This way, only really transparent pixels
// will have zero as a color. // will have zero as a color.
@ -741,15 +719,16 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) {
debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects"); debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects");
Common::SeekableReadStream *stream = openFile(name); Common::SeekableReadStream *stream = openFile(name);
ILBMDecoder decoder(stream, true); ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE);
loader.load(stream, true);
uint16 max = objectsMax[part]; uint16 max = objectsMax[part];
if (_vm->getFeatures() & GF_DEMO) if (_vm->getFeatures() & GF_DEMO)
max = 72; max = 72;
byte *data = new byte[max * 2601]; byte *data = new byte[max * 2601];
byte *srcPtr = decoder.getBitmap(); byte *srcPtr = (byte*)loader._surf->getBasePtr(0,0);
int w = decoder.getWidth(); int w = loader._surf->w;
// Convert to the expected display format // Convert to the expected display format
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
@ -764,7 +743,7 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) {
dst += 51; dst += 51;
} }
} }
free(srcPtr); delete loader._surf;
return new GfxObj(0, new Cnv(max, 51, 51, data, true)); return new GfxObj(0, new Cnv(max, 51, 51, data, true));
} }

View file

@ -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) { void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) {
PaletteFxRange ranges[6];
byte pal[768];
Common::SeekableReadStream *s = openFile(name); Common::SeekableReadStream *s = openFile(name);
BackgroundDecoder decoder(s, true); ILBMLoader loader(&info.bg, pal, ranges);
loader.load(s, true);
PaletteFxRange ranges[6];
memset(ranges, 0, 6*sizeof(PaletteFxRange));
decoder.getCRNG(ranges, 6);
// 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();
info.width = info.bg.w; info.width = info.bg.w;
info.height = info.bg.h; info.height = info.bg.h;
byte *pal = decoder.getPalette();
assert(pal);
byte *p = pal; byte *p = pal;
for (uint i = 0; i < 32; i++) { for (uint i = 0; i < 32; i++) {
byte r = *p >> 2; byte r = *p >> 2;
@ -960,7 +922,6 @@ void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) {
p++; p++;
info.palette.setEntry(i, r, g, b); info.palette.setEntry(i, r, g, b);
} }
delete []pal;
for (uint j = 0; j < 6; j++) { for (uint j = 0; j < 6; j++) {
info.setPaletteRange(j, ranges[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 return; // no errors if missing mask files: not every location has one
} }
ILBMDecoder decoder(s, true); byte pal[768];
byte *pal = decoder.getPalette(); ILBMLoader loader(ILBMLoader::BODYMODE_MASKBUFFER, pal);
assert(pal); loader.load(s, true);
byte r, g, b; byte r, g, b;
for (uint i = 0; i < 4; i++) { 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]; b = pal[i*3+2];
info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF; info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF;
} }
delete []pal;
info._mask = new MaskBuffer; info._mask = loader._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);
} }
void AmigaDisk_ns::loadPath(BackgroundInfo& info, const char *name) { 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 return; // no errors if missing path files: not every location has one
} }
ILBMDecoder decoder(s, true); ILBMLoader loader(ILBMLoader::BODYMODE_PATHBUFFER);
info._path = new PathBuffer; loader.load(s, true);
info._path->create(info.width, info.height); info._path = loader._pathBuffer;
info._path->bigEndian = true; 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) { void AmigaDisk_ns::loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) {

View file

@ -162,9 +162,7 @@ void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) {
Common::Rect rect; Common::Rect rect;
obj->getRect(0, rect); obj->getRect(0, rect);
MaskBuffer *buf = new MaskBuffer; MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height());
buf->create(rect.width(), rect.height());
_vm->_disk->loadMask(name, *buf);
obj->_maskId = addMaskPatch(buf); obj->_maskId = addMaskPatch(buf);
obj->_hasMask = true; obj->_hasMask = true;
@ -174,9 +172,7 @@ void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) {
Common::Rect rect; Common::Rect rect;
obj->getRect(0, rect); obj->getRect(0, rect);
PathBuffer *buf = new PathBuffer; PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height());
buf->create(rect.width(), rect.height());
_vm->_disk->loadPath(name, *buf);
obj->_pathId = addPathPatch(buf); obj->_pathId = addPathPatch(buf);
obj->_hasPath = true; obj->_hasPath = true;

View file

@ -32,34 +32,17 @@
namespace Parallaction { namespace Parallaction {
void IFFParser::setInputStream(Common::SeekableReadStream *stream) { void IFFParser::setInputStream(Common::ReadStream *stream) {
destroy();
assert(stream); assert(stream);
_stream = stream; _formChunk.setInputStream(stream);
_startOffset = 0; _chunk.setInputStream(stream);
_endOffset = _stream->size();
_formType = 0; _formChunk.readHeader();
_formSize = (uint32)-1; if (_formChunk.id != ID_FORM) {
error("IFFParser input is not a FORM type IFF file");
if (_stream->size() < 12) {
// this file is too small to be a valid IFF container
return;
} }
_formSize = _formChunk.size;
if (_stream->readUint32BE() != ID_FORM) { _formType = _formChunk.readUint32BE();
// no FORM header was found
return;
}
_formSize = _stream->readUint32BE();
_formType = _stream->readUint32BE();
}
void IFFParser::destroy() {
_stream = 0;
_startOffset = _endOffset = 0;
} }
uint32 IFFParser::getFORMSize() const { uint32 IFFParser::getFORMSize() const {
@ -70,171 +53,99 @@ Common::IFF_ID IFFParser::getFORMType() const {
return _formType; return _formType;
} }
uint32 IFFParser::moveToIFFBlock(Common::IFF_ID chunkName) { void IFFParser::parse(IFFCallback &callback) {
uint32 size = (uint32)-1; bool stop;
do {
_chunk.feed();
_formChunk.incBytesRead(_chunk.size);
_stream->seek(_startOffset + 0x0C); if (_formChunk.hasReadAll()) {
while ((uint)_stream->pos() < _endOffset) {
uint32 chunk = _stream->readUint32BE();
uint32 size_temp = _stream->readUint32BE();
if (chunk != chunkName) {
_stream->seek((size_temp + 1) & (~1), SEEK_CUR);
assert((uint)_stream->pos() <= _endOffset);
} else {
size = size_temp;
break; break;
} }
}
return size; _formChunk.incBytesRead(8);
} _chunk.readHeader();
uint32 IFFParser::getIFFBlockSize(Common::IFF_ID chunkName) { // invoke the callback
uint32 size = moveToIFFBlock(chunkName); Common::SubReadStream stream(&_chunk, _chunk.size);
return size; IFFChunk chunk(_chunk.id, _chunk.size, &stream);
} stop = callback(chunk);
bool IFFParser::loadIFFBlock(Common::IFF_ID chunkName, void *loadTo, uint32 ptrSize) { // eats up all the remaining data in the chunk
uint32 chunkSize = moveToIFFBlock(chunkName); while (!stream.eos()) {
printf("attemping to eat data in chunk\n");
if (chunkSize == (uint32)-1) { stream.readByte();
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);
} }
// ILBM decoder implementation } while (!stop);
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);
} }
ILBMDecoder::~ILBMDecoder() {
if (_disposeStream) { void ILBMDecoder::loadHeader(Common::ReadStream *stream) {
delete _in; 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);
} }
uint32 ILBMDecoder::getWidth() { void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream) {
assert(_hasHeader); assert(stream);
return _header.width; uint32 numPlanes = MIN(mode & ILBM_UNPACK_PLANES, (uint32)_header.depth);
}
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;
}
byte *ILBMDecoder::getBitmap(uint32 numPlanes, bool packPlanes) {
assert(_bodySize != (uint32)-1);
assert(numPlanes == 1 || numPlanes == 2 || numPlanes == 3 || numPlanes == 4 || numPlanes == 5 || numPlanes == 8); assert(numPlanes == 1 || numPlanes == 2 || numPlanes == 3 || numPlanes == 4 || numPlanes == 5 || numPlanes == 8);
numPlanes = MIN(numPlanes, (uint32)_header.depth); bool packPixels = (mode & ILBM_PACK_PLANES) != 0;
if (numPlanes > 4) { if (numPlanes != 1 && numPlanes != 2 && numPlanes != 4) {
packPlanes = false; packPixels = false;
} }
uint32 bitmapSize = _header.width * _header.height; uint32 outPitch = _header.width;
uint32 bitmapWidth = _header.width; if (packPixels) {
if (packPlanes) { outPitch /= (8 / numPlanes);
bitmapSize /= (8 / numPlanes);
bitmapWidth /= (8 / numPlanes);
} }
byte *out = buffer;
Common::SeekableReadStream *bodyStream = _parser.getIFFBlockStream(ID_BODY);
assert(bodyStream);
byte *bitmap = (byte*)calloc(bitmapSize, 1);
assert(bitmap);
switch (_header.pack) { switch (_header.pack) {
case 1: { // PackBits compressed bitmap case 1: { // PackBits compressed bitmap
Graphics::PackBitsReadStream stream(*bodyStream); Graphics::PackBitsReadStream packStream(*stream);
byte *out = bitmap;
// setup a buffer to hold enough data to build a line in the output // setup a buffer to hold enough data to build a line in the output
uint32 scanWidth = ((_header.width + 15)/16) << 1; uint32 scanlineWidth = ((_header.width + 15)/16) << 1;
byte *scanBuffer = (byte*)malloc(scanWidth * _header.depth); byte *scanline = new byte[scanlineWidth * _header.depth];
for (uint i = 0; i < _header.height; ++i) { for (uint i = 0; i < _header.height; ++i) {
byte *s = scanBuffer; byte *s = scanline;
for (uint32 j = 0; j < _header.depth; ++j) { for (uint32 j = 0; j < _header.depth; ++j) {
stream.read(s, scanWidth); packStream.read(s, scanlineWidth);
s += scanWidth; s += scanlineWidth;
} }
planarToChunky(out, bitmapWidth, scanBuffer, scanWidth, numPlanes, packPlanes); planarToChunky(out, outPitch, scanline, scanlineWidth, numPlanes, packPixels);
out += bitmapWidth; out += outPitch;
} }
free(scanBuffer); delete []scanline;
break; break;
} }
default: default:
// implement other compression types here!
error("only RLE compressed ILBM files are supported"); error("only RLE compressed ILBM files are supported");
break; break;
} }
delete bodyStream;
return bitmap;
} }
void ILBMDecoder::planarToChunky(byte *out, uint32 outPitch, byte *in, uint32 inWidth, uint32 nPlanes, bool packPlanes) {
void ILBMDecoder::planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes) {
byte pix, ofs, bit; byte pix, ofs, bit;
byte *s; byte *s;
uint32 pixels = width; uint32 pixels = outPitch;
if (packPlanes) { if (packPlanes) {
pixels *= (8 / nPlanes); pixels *= (8 / nPlanes);
} }
@ -251,7 +162,7 @@ void ILBMDecoder::planarToChunky(byte *out, uint32 width, byte *in, uint32 plane
if (s[ofs] & bit) { if (s[ofs] & bit) {
pix |= (1 << plane); pix |= (1 << plane);
} }
s += planeWidth; s += inWidth;
} }

View file

@ -27,39 +27,126 @@
#define PARALLACTION_IFF_H #define PARALLACTION_IFF_H
#include "common/stream.h" #include "common/stream.h"
#include "common/func.h"
#include "common/iff_container.h" // for IFF chunk names #include "common/iff_container.h" // for IFF chunk names
#include "graphics/iff.h" // for BMHD #include "graphics/iff.h" // for BMHD
// this IFF parser code is courtesy of the Kyra engine team ;)
namespace Parallaction { 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 { class IFFParser {
/**
* This private class implements IFF chunk navigation.
*/
class IFFChunkNav : public Common::ReadStream {
protected:
Common::ReadStream *_input;
uint32 _bytesRead;
public: public:
IFFParser() : _stream(0), _startOffset(0), _endOffset(0) {} Common::IFF_ID id;
IFFParser(Common::SeekableReadStream *stream) : _stream(0), _startOffset(0), _endOffset(0) { 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(Common::ReadStream *stream, bool disposeStream = false) : _stream(stream), _disposeStream(stream) {
setInputStream(stream); setInputStream(stream);
} }
~IFFParser() { destroy(); } ~IFFParser() {
if (_disposeStream) {
delete _stream;
}
_stream = 0;
}
void setInputStream(Common::SeekableReadStream *stream); /**
* Returns the IFF FORM type.
operator bool() const { return (_startOffset != _endOffset) && _stream; } * @return the IFF FORM type of the stream, or 0 if FORM header is not found.
*/
uint32 getFORMSize() const;
Common::IFF_ID getFORMType() const; Common::IFF_ID getFORMType() const;
uint32 getIFFBlockSize(Common::IFF_ID chunk); /**
bool loadIFFBlock(Common::IFF_ID chunk, void *loadTo, uint32 ptrSize); * Returns the size of the data.
Common::SeekableReadStream *getIFFBlockStream(Common::IFF_ID chunkName); * @return the size of the data in file, or -1 if FORM header is not found.
*/
uint32 getFORMSize() const;
/**
* Callback type for the parser.
*/
typedef Common::Functor1< IFFChunk&, bool > IFFCallback;
/**
* 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: private:
void destroy();
uint32 moveToIFFBlock(Common::IFF_ID chunkName);
Common::SeekableReadStream *_stream;
uint32 _startOffset;
uint32 _endOffset;
uint32 _formSize; uint32 _formSize;
Common::IFF_ID _formType; Common::IFF_ID _formType;
}; };
@ -67,35 +154,49 @@ private:
class ILBMDecoder { struct ILBMDecoder {
Common::SeekableReadStream *_in; /**
bool _disposeStream; * ILBM header data, necessary for loadBitmap()
*/
void planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes);
protected:
IFFParser _parser;
Graphics::BMHD _header; Graphics::BMHD _header;
bool _hasHeader;
uint32 _bodySize;
uint32 _paletteSize;
/**
* 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.
public: ILBM_1_PLANES = 1, //!< Decode only the first bitplane, don't pack.
ILBMDecoder(Common::SeekableReadStream *input, bool disposeStream = false); 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.
};
virtual ~ILBMDecoder(); /**
* Fills the _header member from the given stream.
*/
void loadHeader(Common::ReadStream *stream);
uint32 getWidth(); /**
uint32 getHeight(); * Loads and unpacks the ILBM bitmap data from the stream into the buffer.
uint32 getNumColors(); * The functions assumes the buffer is large enough to contain all data.
byte *getPalette(); * The caller controls how data should be packed by choosing mode from
* the enum above.
*/
void loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream);
byte *getBitmap(uint32 numPlanes, bool packPlanes); /**
byte *getBitmap() { * Converts from bitplanar to chunky representation. Intended for internal
assert(_hasHeader); * usage, but you can be (ab)use it from client code if you know what you
return getBitmap(_header.depth, false); * are doing.
} */
void planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes);
}; };

View file

@ -7,6 +7,7 @@ MODULE_OBJS := \
debug.o \ debug.o \
detection.o \ detection.o \
dialogue.o \ dialogue.o \
disk.o \
disk_br.o \ disk_br.o \
disk_ns.o \ disk_ns.o \
exec.o \ exec.o \