void DarkMoonEngine::loadMonsterDecoration(const char *file, int16 monsterIndex) { Common::SeekableReadStream *s = _res->createReadStream(Common::String::format("%s.dcr", file)); if (!s) return; int len = s->readUint16LE(); for (int i = 0; i < len; i++) { for (int ii = 0; ii < 6; ii++) { uint8 dc[6]; s->read(dc, 6); if (!dc[2] || !dc[3]) continue; SpriteDecoration *m = &_monsterDecorations[i * 6 + ii + monsterIndex]; m->shp = _screen->encodeShape(dc[0], dc[1], dc[2], dc[3]); m->x = (int8)dc[4]; m->y = (int8)dc[5]; } } delete s; }
bool StaticResource::loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size) { size = stream.size() / 28; SpellProperty *spellData = new SpellProperty[size]; for (int i = 0; i < size; i++) { SpellProperty *t = &spellData[i]; t->spellNameCode = stream.readUint16LE(); for (int ii = 0; ii < 4; ii++) t->mpRequired[ii] = stream.readUint16LE(); t->field_a = stream.readUint16LE(); t->field_c = stream.readUint16LE(); for (int ii = 0; ii < 4; ii++) t->hpRequired[ii] = stream.readUint16LE(); t->field_16 = stream.readUint16LE(); t->field_18 = stream.readUint16LE(); t->flags = stream.readUint16LE(); }; ptr = spellData; return true; }
bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_) { const int32 initialPos = stream.pos(); byte buf[4+1]; buf[4] = 0; stream.read(buf, 4); if (memcmp(buf, "RIFF", 4) != 0) { warning("getWavInfo: No 'RIFF' header"); return false; } int32 wavLength = stream.readUint32LE(); stream.read(buf, 4); if (memcmp(buf, "WAVE", 4) != 0) { warning("getWavInfo: No 'WAVE' header"); return false; } stream.read(buf, 4); if (memcmp(buf, "fmt ", 4) != 0) { warning("getWavInfo: No 'fmt' header"); return false; } uint32 fmtLength = stream.readUint32LE(); if (fmtLength < 16) { // A valid fmt chunk always contains at least 16 bytes warning("getWavInfo: 'fmt' header is too short"); return false; } // Next comes the "type" field of the fmt header. Some typical // values for it: // 1 -> uncompressed PCM // 17 -> IMA ADPCM compressed WAVE // See <http://www.saettler.com/RIFFNEW/RIFFNEW.htm> for a more complete // list of common WAVE compression formats... uint16 type = stream.readUint16LE(); // == 1 for PCM data uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo uint32 samplesPerSec = stream.readUint32LE(); // in Hz uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8 uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8 uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ... // 8 bit data is unsigned, 16 bit data signed if (wavType != 0) *wavType = type; if (blockAlign_ != 0) *blockAlign_ = blockAlign; #if 0 debug("WAVE information:"); debug(" total size: %d", wavLength); debug(" fmt size: %d", fmtLength); debug(" type: %d", type); debug(" numChannels: %d", numChannels); debug(" samplesPerSec: %d", samplesPerSec); debug(" avgBytesPerSec: %d", avgBytesPerSec); debug(" blockAlign: %d", blockAlign); debug(" bitsPerSample: %d", bitsPerSample); #endif if (type != 1 && type != 2 && type != 17) { warning("getWavInfo: only PCM, MS ADPCM or IMA ADPCM data is supported (type %d)", type); return false; } if (blockAlign != numChannels * bitsPerSample / 8 && type != 2) { debug(0, "getWavInfo: blockAlign is invalid"); } if (avgBytesPerSec != samplesPerSec * blockAlign && type != 2) { debug(0, "getWavInfo: avgBytesPerSec is invalid"); } // Prepare the return values. rate = samplesPerSec; flags = 0; if (bitsPerSample == 8) // 8 bit data is unsigned flags |= Audio::FLAG_UNSIGNED; else if (bitsPerSample == 16) // 16 bit data is signed little endian flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); else if (bitsPerSample == 4 && (type == 2 || type == 17)) flags |= Audio::FLAG_16BITS; else { warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample); return false; } if (numChannels == 2) flags |= Audio::FLAG_STEREO; else if (numChannels != 1) { warning("getWavInfo: unsupported number of channels %d", numChannels); return false; } // It's almost certainly a WAV file, but we still need to find its // 'data' chunk. // Skip over the rest of the fmt chunk. int offset = fmtLength - 16; do { stream.seek(offset, SEEK_CUR); if (stream.pos() >= initialPos + wavLength + 8) { warning("getWavInfo: Can't find 'data' chunk"); return false; } stream.read(buf, 4); offset = stream.readUint32LE(); #if 0 debug(" found a '%s' tag of size %d", buf, offset); #endif } while (memcmp(buf, "data", 4) != 0); // Stream now points at 'offset' bytes of sample data... size = offset; return true; }
bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) { uint16 version = stream.readUint16LE(); // We'll accept Win1, Win2, and Win3 fonts if (version != 0x100 && version != 0x200 && version != 0x300) { warning("Bad FNT version %04x", version); return false; } /* uint32 size = */ stream.readUint32LE(); stream.skip(60); // Copyright info uint16 fontType = stream.readUint16LE(); /* uint16 points = */ stream.readUint16LE(); /* uint16 vertRes = */ stream.readUint16LE(); /* uint16 horizRes = */ stream.readUint16LE(); /* uint16 ascent = */ stream.readUint16LE(); /* uint16 internalLeading = */ stream.readUint16LE(); /* uint16 externalLeading = */ stream.readUint16LE(); /* byte italic = */ stream.readByte(); /* byte underline = */ stream.readByte(); /* byte strikeOut = */ stream.readByte(); /* uint16 weight = */ stream.readUint16LE(); /* byte charSet = */ stream.readByte(); uint16 pixWidth = stream.readUint16LE(); _pixHeight = stream.readUint16LE(); /* byte pitchAndFamily = */ stream.readByte(); /* uint16 avgWidth = */ stream.readUint16LE(); _maxWidth = stream.readUint16LE(); _firstChar = stream.readByte(); _lastChar = stream.readByte(); _defaultChar = stream.readByte(); /* byte breakChar = */ stream.readByte(); /* uint16 widthBytes = */ stream.readUint16LE(); /* uint32 device = */ stream.readUint32LE(); /* uint32 face = */ stream.readUint32LE(); /* uint32 bitsPointer = */ stream.readUint32LE(); uint32 bitsOffset = stream.readUint32LE(); /* byte reserved = */ stream.readByte(); if (version == 0x100) { // Seems Win1 has an extra byte? stream.readByte(); } else if (version == 0x300) { // For Windows 3.0, Microsoft added 6 new fields. All of which are // guaranteed to be 0. Which leads to the question: Why add these at all? /* uint32 flags = */ stream.readUint32LE(); /* uint16 aSpace = */ stream.readUint16LE(); /* uint16 bSpace = */ stream.readUint16LE(); /* uint16 cSpace = */ stream.readUint16LE(); /* uint32 colorPointer = */ stream.readUint32LE(); stream.skip(16); // Reserved } // Begin loading in the glyphs _glyphCount = (_lastChar - _firstChar) + 2; _glyphs = new GlyphEntry[_glyphCount]; for (uint16 i = 0; i < _glyphCount; i++) { _glyphs[i].charWidth = stream.readUint16LE(); // Use the default if present if (pixWidth) _glyphs[i].charWidth = pixWidth; _glyphs[i].offset = (version == 0x300) ? stream.readUint32LE() : stream.readUint16LE(); // Seems the offsets in the Win1 font format are based on bitsOffset if (version == 0x100) _glyphs[i].offset += bitsOffset; } // TODO: Currently only raster fonts are supported! if (fontType & 1) { warning("Vector FNT files not supported yet"); return false; } // Read in the bitmaps for the raster images for (uint16 i = 0; i < _glyphCount - 1; i++) { stream.seek(_glyphs[i].offset); _glyphs[i].bitmap = new byte[_pixHeight * _glyphs[i].charWidth]; // Calculate the amount of columns byte colCount = (_glyphs[i].charWidth + 7) / 8; for (uint16 j = 0; j < colCount; j++) { for (uint16 k = 0; k < _pixHeight; k++) { byte x = stream.readByte(); uint offset = j * 8 + k * _glyphs[i].charWidth; for (byte l = 0; l < 8 && j * 8 + l < _glyphs[i].charWidth; l++) _glyphs[i].bitmap[offset + l] = (x & (1 << (7 - l))) ? 1 : 0; } } #if 0 // Debug print debug("Character %02x '%c' at %08x", indexToCharacter(i), indexToCharacter(i), _glyphs[i].offset); for (uint16 j = 0; j < _pixHeight; j++) { for (uint16 k = 0; k < _glyphs[i].charWidth; k++) debugN("%c", _glyphs[i].bitmap[k + j * _glyphs[i].charWidth] ? 'X' : ' '); debugN("\n"); } #endif } return true; }
void CUP_Player::handleHEAD(Common::SeekableReadStream &dataStream, uint32 dataSize) { _playbackRate = dataStream.readUint16LE(); _width = dataStream.readUint16LE(); _height = dataStream.readUint16LE(); }
void TPC::readHeader(Common::SeekableReadStream &tpc, byte &encoding) { // Number of bytes for the pixel data in one full image uint32 dataSize = tpc.readUint32LE(); tpc.skip(4); // Some float // Image dimensions uint32 width = tpc.readUint16LE(); uint32 height = tpc.readUint16LE(); if ((width >= 0x8000) || (height >= 0x8000)) throw Common::Exception("Unsupported image dimensions (%ux%u)", width, height); // How's the pixel data encoded? encoding = tpc.readByte(); // Number of mip maps in the image size_t mipMapCount = tpc.readByte(); tpc.skip(114); // Reserved uint32 minDataSize = 0; if (dataSize == 0) { // Uncompressed if (encoding == kEncodingGray) { // 8bpp grayscale _format = kPixelFormatR8G8B8; minDataSize = 1; dataSize = width * height; } else if (encoding == kEncodingRGB) { // RGB, no alpha channel _format = kPixelFormatR8G8B8; minDataSize = 3; dataSize = width * height * 3; } else if (encoding == kEncodingRGBA) { // RGBA, alpha channel _format = kPixelFormatR8G8B8A8; minDataSize = 4; dataSize = width * height * 4; } else if (encoding == kEncodingSwizzledBGRA) { // BGRA, alpha channel, texture memory layout is "swizzled" _format = kPixelFormatB8G8R8A8; minDataSize = 4; dataSize = width * height * 4; } else throw Common::Exception("Unknown TPC raw encoding: %d (%d), %dx%d, %u", encoding, dataSize, width, height, (uint) mipMapCount); } else if (encoding == kEncodingRGB) { // S3TC DXT1 _format = kPixelFormatDXT1; minDataSize = 8; checkCubeMap(width, height); if (dataSize != ((width * height) / 2)) throw Common::Exception("Invalid data size for a texture of %ux%u pixels and format %u", width, height, encoding); } else if (encoding == kEncodingRGBA) { // S3TC DXT5 _format = kPixelFormatDXT5; minDataSize = 16; checkCubeMap(width, height); if (dataSize != (width * height)) throw Common::Exception("Invalid data size for a texture of %ux%u pixels and format %u", width, height, encoding); } else throw Common::Exception("Unknown TPC encoding: %d (%d)", encoding, dataSize); if (!hasValidDimensions(_format, width, height)) throw Common::Exception("Invalid dimensions (%dx%d) for format %d", width, height, _format); const size_t fullImageDataSize = getDataSize(_format, width, height); size_t fullDataSize = tpc.size() - 128; if (fullDataSize < (_layerCount * fullImageDataSize)) throw Common::Exception("Image wouldn't fit into data"); _mipMaps.reserve(mipMapCount * _layerCount); size_t layerCount; for (layerCount = 0; layerCount < _layerCount; layerCount++) { uint32 layerWidth = width; uint32 layerHeight = height; uint32 layerSize = dataSize; for (size_t i = 0; i < mipMapCount; i++) { MipMap *mipMap = new MipMap; mipMap->width = MAX<uint32>(layerWidth, 1); mipMap->height = MAX<uint32>(layerHeight, 1); mipMap->size = MAX<uint32>(layerSize, minDataSize); mipMap->data = 0; const size_t mipMapDataSize = getDataSize(_format, mipMap->width, mipMap->height); if ((fullDataSize < mipMap->size) || (mipMap->size < mipMapDataSize)) { // Wouldn't fit delete mipMap; break; } fullDataSize -= mipMap->size; _mipMaps.push_back(mipMap); layerWidth >>= 1; layerHeight >>= 1; layerSize >>= 2; if ((layerWidth < 1) && (layerHeight < 1)) break; } } if ((layerCount != _layerCount) || ((_mipMaps.size() % _layerCount) != 0)) throw Common::Exception("Failed to correctly read all texture layers (%u, %u, %u, %u)", (uint) _layerCount, (uint) mipMapCount, (uint) layerCount, (uint) _mipMaps.size()); }
bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) { size = stream.size() / 130; LoLCharacter *charData = new LoLCharacter[size]; for (int i = 0; i < size; i++) { LoLCharacter *t = &charData[i]; t->flags = stream.readUint16LE(); stream.read(t->name, 11); t->raceClassSex = stream.readByte(); t->id = stream.readSint16LE(); t->curFaceFrame = stream.readByte(); t->tempFaceFrame = stream.readByte(); t->screamSfx = stream.readByte(); stream.readUint32LE(); for (int ii = 0; ii < 8; ii++) t->itemsMight[ii] = stream.readUint16LE(); for (int ii = 0; ii < 8; ii++) t->protectionAgainstItems[ii] = stream.readUint16LE(); t->itemProtection = stream.readUint16LE(); t->hitPointsCur = stream.readSint16LE(); t->hitPointsMax = stream.readUint16LE(); t->magicPointsCur = stream.readSint16LE(); t->magicPointsMax = stream.readUint16LE(); t->field_41 = stream.readByte(); t->damageSuffered = stream.readUint16LE(); t->weaponHit = stream.readUint16LE(); t->totalMightModifier = stream.readUint16LE(); t->totalProtectionModifier = stream.readUint16LE(); t->might = stream.readUint16LE(); t->protection = stream.readUint16LE(); t->nextAnimUpdateCountdown = stream.readSint16LE(); for (int ii = 0; ii < 11; ii++) t->items[ii] = stream.readUint16LE(); for (int ii = 0; ii < 3; ii++) t->skillLevels[ii] = stream.readByte(); for (int ii = 0; ii < 3; ii++) t->skillModifiers[ii] = stream.readByte(); for (int ii = 0; ii < 3; ii++) t->experiencePts[ii] = stream.readUint32LE(); for (int ii = 0; ii < 5; ii++) t->characterUpdateEvents[ii] = stream.readByte(); for (int ii = 0; ii < 5; ii++) t->characterUpdateDelay[ii] = stream.readByte(); }; ptr = charData; return true; }
void TPC::readHeader(Common::SeekableReadStream &tpc, byte &encoding) { // Number of bytes for the pixel data in one full image uint32 dataSize = tpc.readUint32LE(); tpc.skip(4); // Some float // Image dimensions uint32 width = tpc.readUint16LE(); uint32 height = tpc.readUint16LE(); // How's the pixel data encoded? encoding = tpc.readByte(); // Number of mip maps in the image byte mipMapCount = tpc.readByte(); tpc.skip(114); // Reserved uint32 minDataSize = 0; if (dataSize == 0) { // Uncompressed if (encoding == kEncodingGray) { // 8bpp grayscale _format = kPixelFormatR8G8B8; minDataSize = 1; dataSize = width * height; } else if (encoding == kEncodingRGB) { // RGB, no alpha channel _format = kPixelFormatR8G8B8; minDataSize = 3; dataSize = width * height * 3; } else if (encoding == kEncodingRGBA) { // RGBA, alpha channel _format = kPixelFormatR8G8B8A8; minDataSize = 4; dataSize = width * height * 4; } else if (encoding == kEncodingSwizzledBGRA) { // BGRA, alpha channel, texture memory layout is "swizzled" _format = kPixelFormatB8G8R8A8; minDataSize = 4; dataSize = width * height * 4; } else throw Common::Exception("Unknown TPC raw encoding: %d (%d), %dx%d, %d", encoding, dataSize, width, height, mipMapCount); } else if (encoding == kEncodingRGB) { // S3TC DXT1 _format = kPixelFormatDXT1; minDataSize = 8; } else if (encoding == kEncodingRGBA) { // S3TC DXT5 _format = kPixelFormatDXT5; minDataSize = 16; } else throw Common::Exception("Unknown TPC encoding: %d (%d)", encoding, dataSize); size_t fullDataSize = tpc.size() - 128; _mipMaps.reserve(mipMapCount); for (uint32 i = 0; i < mipMapCount; i++) { MipMap *mipMap = new MipMap; mipMap->width = MAX<uint32>(width, 1); mipMap->height = MAX<uint32>(height, 1); mipMap->size = MAX<uint32>(dataSize, minDataSize); mipMap->data = 0; if (fullDataSize < mipMap->size) { // Wouldn't fit delete mipMap; break; } fullDataSize -= mipMap->size; _mipMaps.push_back(mipMap); width >>= 1; height >>= 1; dataSize >>= 2; if ((width < 1) && (height < 1)) break; } }
void SmushPlayer::handleIACT(int32 subSize, Common::SeekableReadStream &b) { debugC(DEBUG_SMUSH, "SmushPlayer::IACT()"); assert(subSize >= 8); int code = b.readUint16LE(); int flags = b.readUint16LE(); int unknown = b.readSint16LE(); int track_flags = b.readUint16LE(); if ((code != 8) && (flags != 46)) { _vm->_insane->procIACT(_dst, 0, 0, 0, b, 0, 0, code, flags, unknown, track_flags); return; } if (_compressedFileMode) { return; } assert(flags == 46 && unknown == 0); int track_id = b.readUint16LE(); int index = b.readUint16LE(); int nbframes = b.readUint16LE(); int32 size = b.readUint32LE(); int32 bsize = subSize - 18; if (_vm->_game.id != GID_CMI) { int32 track = track_id; if (track_flags == 1) { track = track_id + 100; } else if (track_flags == 2) { track = track_id + 200; } else if (track_flags == 3) { track = track_id + 300; } else if ((track_flags >= 100) && (track_flags <= 163)) { track = track_id + 400; } else if ((track_flags >= 200) && (track_flags <= 263)) { track = track_id + 500; } else if ((track_flags >= 300) && (track_flags <= 363)) { track = track_id + 600; } else { error("SmushPlayer::handleIACT(): bad track_flags: %d", track_flags); } debugC(DEBUG_SMUSH, "SmushPlayer::handleIACT(): %d, %d, %d", track, index, track_flags); SmushChannel *c = _smixer->findChannel(track); if (c == 0) { c = new ImuseChannel(track); _smixer->addChannel(c); } if (index == 0) c->setParameters(nbframes, size, track_flags, unknown, 0); else c->checkParameters(index, nbframes, size, track_flags, unknown); c->appendData(b, bsize); } else { // TODO: Move this code into another SmushChannel subclass? byte *src = (byte *)malloc(bsize); b.read(src, bsize); byte *d_src = src; byte value; while (bsize > 0) { if (_IACTpos >= 2) { int32 len = READ_BE_UINT16(_IACToutput) + 2; len -= _IACTpos; if (len > bsize) { memcpy(_IACToutput + _IACTpos, d_src, bsize); _IACTpos += bsize; bsize = 0; } else { byte *output_data = (byte *)malloc(4096); memcpy(_IACToutput + _IACTpos, d_src, len); byte *dst = output_data; byte *d_src2 = _IACToutput; d_src2 += 2; int32 count = 1024; byte variable1 = *d_src2++; byte variable2 = variable1 / 16; variable1 &= 0x0f; do { value = *(d_src2++); if (value == 0x80) { *dst++ = *d_src2++; *dst++ = *d_src2++; } else { int16 val = (int8)value << variable2; *dst++ = val >> 8; *dst++ = (byte)(val); } value = *(d_src2++); if (value == 0x80) { *dst++ = *d_src2++; *dst++ = *d_src2++; } else { int16 val = (int8)value << variable1; *dst++ = val >> 8; *dst++ = (byte)(val); } } while (--count); if (!_IACTstream) { _IACTstream = Audio::makeQueuingAudioStream(22050, true); _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream); } _IACTstream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS); bsize -= len; d_src += len; _IACTpos = 0; } } else { if (bsize > 1 && _IACTpos == 0) { *(_IACToutput + 0) = *d_src++; _IACTpos = 1; bsize--; } *(_IACToutput + _IACTpos) = *d_src++; _IACTpos++; bsize--; } }
bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { // RLE-TrueColor / RLE-Black/White if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) { _surface.create(_surface.w, _surface.h, _format); uint32 count = _surface.w * _surface.h; byte *data = (byte *)_surface.getPixels(); while (count > 0) { uint32 header = tga.readByte(); byte type = (header & 0x80) >> 7; uint32 rleCount = (header & 0x7F) + 1; // RLE-packet if (type == 1) { if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { uint32 color = tga.readUint32LE(); while (rleCount-- > 0) { *((uint32 *)data) = color; data += 4; count--; } } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { byte r = tga.readByte(); byte g = tga.readByte(); byte b = tga.readByte(); while (rleCount-- > 0) { #ifdef SCUMM_LITTLE_ENDIAN *data++ = r; *data++ = g; *data++ = b; #else *data++ = b; *data++ = g; *data++ = r; #endif count--; } } else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) { const uint16 rgb = tga.readUint16LE(); while (rleCount-- > 0) { *((uint16 *)data) = rgb; data += 2; count--; } } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { byte color = tga.readByte(); while (rleCount-- > 0) { *data++ = color; *data++ = color; *data++ = color; *data++ = color; count--; } } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { byte index = tga.readByte(); while (rleCount-- > 0) { *data++ = index; count--; } } else { warning("Unhandled pixel-depth for image-type 10"); return false; } // Raw-packet } else if (type == 0) { if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { while (rleCount-- > 0) { uint32 color = tga.readUint32LE(); *((uint32 *)data) = color; data += 4; count--; } } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { while (rleCount-- > 0) { byte r = tga.readByte(); byte g = tga.readByte(); byte b = tga.readByte(); #ifdef SCUMM_LITTLE_ENDIAN *data++ = r; *data++ = g; *data++ = b; #else *data++ = b; *data++ = g; *data++ = r; #endif count--; } } else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) { while (rleCount-- > 0) { *((uint16 *)data) = tga.readUint16LE(); data += 2; count--; } } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { while (rleCount-- > 0) { byte color = tga.readByte(); *data++ = color; *data++ = color; *data++ = color; *data++ = color; count--; } } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { while (rleCount-- > 0) { byte index = tga.readByte(); *data++ = index; count--; } } else { warning("Unhandled pixel-depth for image-type 10"); return false; } } else { warning("Unknown header for RLE-packet %d", type); return false; } } } else {
// Additional information found from http://paulbourke.net/dataformats/tga/ // With some details from the link referenced in the header. bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { // TrueColor if (imageType == TYPE_TRUECOLOR) { _surface.create(_surface.w, _surface.h, _format); if (pixelDepth == 16) { for (int i = 0; i < _surface.h; i++) { uint16 *dst; if (!_originTop) { dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1); } else { dst = (uint16 *)_surface.getBasePtr(0, i); } for (int j = 0; j < _surface.w; j++) { *dst++ = tga.readUint16LE(); } } } else if (pixelDepth == 32) { for (int i = 0; i < _surface.h; i++) { uint32 *dst; if (!_originTop) { dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1); } else { dst = (uint32 *)_surface.getBasePtr(0, i); } for (int j = 0; j < _surface.w; j++) { *dst++ = tga.readUint32LE(); } } } else if (pixelDepth == 24) { for (int i = 0; i < _surface.h; i++) { byte *dst; if (!_originTop) { dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); } else { dst = (byte *)_surface.getBasePtr(0, i); } for (int j = 0; j < _surface.w; j++) { byte r = tga.readByte(); byte g = tga.readByte(); byte b = tga.readByte(); #ifdef SCUMM_LITTLE_ENDIAN *dst++ = r; *dst++ = g; *dst++ = b; #else *dst++ = b; *dst++ = g; *dst++ = r; #endif } } } // Black/White } else if (imageType == TYPE_BW) { _surface.create(_surface.w, _surface.h, _format); byte *data = (byte *)_surface.getPixels(); uint32 count = _surface.w * _surface.h; while (count-- > 0) { byte g = tga.readByte(); *data++ = g; *data++ = g; *data++ = g; *data++ = g; } } return true; }
Common::Point GameModule::readPoint(Common::SeekableReadStream &s) { Common::Point p; p.x = s.readUint16LE(); p.y = s.readUint16LE(); return p; }
bool WinCursor::readFromStream(Common::SeekableReadStream &stream) { clear(); _hotspotX = stream.readUint16LE(); _hotspotY = stream.readUint16LE(); // Check header size if (stream.readUint32LE() != 40) return false; // Check dimensions _width = stream.readUint32LE(); _height = stream.readUint32LE() / 2; if (_width & 3) { // Cursors should always be a power of 2 // Of course, it wouldn't be hard to handle but if we have no examples... warning("Non-divisible-by-4 width cursor found"); return false; } // Color planes if (stream.readUint16LE() != 1) return false; // Only 1bpp and 8bpp supported uint16 bitsPerPixel = stream.readUint16LE(); if (bitsPerPixel != 1 && bitsPerPixel != 8) return false; // Compression if (stream.readUint32LE() != 0) return false; // Image size + X resolution + Y resolution stream.skip(12); uint32 numColors = stream.readUint32LE(); // If the color count is 0, then it uses up the maximum amount if (numColors == 0) numColors = 1 << bitsPerPixel; // Reading the palette stream.seek(40 + 4); for (uint32 i = 0 ; i < numColors; i++) { _palette[i * 3 + 2] = stream.readByte(); _palette[i * 3 + 1] = stream.readByte(); _palette[i * 3 ] = stream.readByte(); stream.readByte(); } // Reading the bitmap data uint32 dataSize = stream.size() - stream.pos(); byte *initialSource = new byte[dataSize]; stream.read(initialSource, dataSize); // Parse the XOR map const byte *src = initialSource; _surface = new byte[_width * _height]; byte *dest = _surface + _width * (_height - 1); uint32 imagePitch = _width * bitsPerPixel / 8; for (uint32 i = 0; i < _height; i++) { byte *rowDest = dest; if (bitsPerPixel == 1) { // 1bpp for (uint16 j = 0; j < (_width / 8); j++) { byte p = src[j]; for (int k = 0; k < 8; k++, rowDest++, p <<= 1) { if ((p & 0x80) == 0x80) *rowDest = 1; else *rowDest = 0; } } } else { // 8bpp memcpy(rowDest, src, _width); } dest -= _width; src += imagePitch; } // Calculate our key color if (numColors < 256) { // If we're not using the maximum colors in a byte, we can fit it in _keyColor = numColors; } else { // HACK: Try to find a color that's not being used so it can become // our keycolor. It's quite impossible to fit 257 entries into 256... for (uint32 i = 0; i < 256; i++) { for (int j = 0; j < _width * _height; j++) { // TODO: Also check to see if the space is transparent if (_surface[j] == i) break; if (j == _width * _height - 1) { _keyColor = i; i = 256; break; } } } } // Now go through and apply the AND map to get the transparency uint32 andWidth = (_width + 7) / 8; src += andWidth * (_height - 1); for (uint32 y = 0; y < _height; y++) { for (uint32 x = 0; x < _width; x++) if (src[x / 8] & (1 << (7 - x % 8))) _surface[y * _width + x] = _keyColor; src -= andWidth; } delete[] initialSource; return true; }
void ConversationData::load(const Common::String &filename) { Common::File inFile; char buffer[16]; inFile.open(filename); MadsPack convFileUnpacked(&inFile); Common::SeekableReadStream *convFile = convFileUnpacked.getItemStream(0); // **** Section 0: Header ************************************************* _nodeCount = convFile->readUint16LE(); _dialogCount = convFile->readUint16LE(); _messageCount = convFile->readUint16LE(); _textLineCount = convFile->readUint16LE(); _unk2 = convFile->readUint16LE(); _importCount = convFile->readUint16LE(); _speakerCount = convFile->readUint16LE(); for (uint idx = 0; idx < MAX_SPEAKERS; ++idx) { convFile->read(buffer, 16); _portraits[idx] = buffer; } for (uint idx = 0; idx < MAX_SPEAKERS; ++idx) { _speakerExists[idx] = convFile->readUint16LE(); } convFile->read(buffer, 14); _speechFile = Common::String(buffer); // Total text length in section 5 _textSize = convFile->readUint32LE(); _commandsSize = convFile->readUint32LE(); // The rest of the section 0 is padding to allow room for a set of pointers // to the contents of the remaining sections loaded into memory as a // continuous data block containing both the header and the sections delete convFile; // **** Section 1: Nodes ************************************************** convFile = convFileUnpacked.getItemStream(1); _convNodes.clear(); for (uint i = 0; i < _nodeCount; i++) { ConvNode node; node._index = convFile->readUint16LE(); node._dialogCount = convFile->readUint16LE(); node._unk1 = convFile->readSint16LE(); // TODO node._unk2 = convFile->readSint16LE(); // TODO node._unk3 = convFile->readSint16LE(); // TODO _convNodes.push_back(node); //debug("Node %d, index %d, entries %d - %d, %d, %d", i, node.index, node.dialogCount, node.unk1, node.unk2, node.unk3); } delete convFile; // **** Section 2: Dialogs ************************************************ convFile = convFileUnpacked.getItemStream(2); assert(convFile->size() == _dialogCount * 8); for (uint idx = 0; idx < _nodeCount; ++idx) { uint dialogCount = _convNodes[idx]._dialogCount; for (uint j = 0; j < dialogCount; ++j) { ConvDialog dialog; dialog._textLineIndex = convFile->readSint16LE(); dialog._speechIndex = convFile->readSint16LE(); dialog._nodeOffset = convFile->readUint16LE(); dialog._nodeSize = convFile->readUint16LE(); _convNodes[idx]._dialogs.push_back(dialog); } } delete convFile; // **** Section 3: Messages *********************************************** convFile = convFileUnpacked.getItemStream(3); assert(convFile->size() == _messageCount * 8); _messages.resize(_messageCount); for (uint idx = 0; idx < _messageCount; ++idx) _messages[idx] = convFile->readUint32LE(); delete convFile; // **** Section 4: Text line offsets ************************************** convFile = convFileUnpacked.getItemStream(4); assert(convFile->size() == _textLineCount * 2); uint16 *textLineOffsets = new uint16[_textLineCount]; // deleted below in section 5 for (uint16 i = 0; i < _textLineCount; i++) textLineOffsets[i] = convFile->readUint16LE(); delete convFile; // **** Section 5: Text lines ********************************************* convFile = convFileUnpacked.getItemStream(5); assert(convFile->size() == _textSize); Common::String textLine; _textLines.resize(_textLineCount); char textLineBuffer[256]; uint16 nextOffset; for (uint16 i = 0; i < _textLineCount; i++) { nextOffset = (i != _textLineCount - 1) ? textLineOffsets[i + 1] : convFile->size(); convFile->read(textLineBuffer, nextOffset - textLineOffsets[i]); _textLines[i] = Common::String(textLineBuffer); } delete[] textLineOffsets; delete convFile; // **** Section 6: Node entry commands ************************************ convFile = convFileUnpacked.getItemStream(6); assert(convFile->size() == _commandsSize); for (uint16 i = 0; i < _nodeCount; i++) { uint16 dialogCount = _convNodes[i]._dialogCount; for (uint16 j = 0; j < dialogCount; j++) { //ConvDialog dialog = _convNodes[i].dialogs[j]; byte command; uint16 chk; do { command = convFile->readByte(); chk = convFile->readUint16BE(); if (chk != 0xFF00 && chk != 0x0000) { warning("Error while reading conversation node entries - bailing out"); break; } switch (command) { case cmdNodeEnd: //debug("Node end"); break; case cmdDialogEnd: //debug("Dialog end"); break; case cmdHide: { byte count = convFile->readByte(); for (byte k = 0; k < count; k++) { /*uint16 nodeRef = */convFile->readUint16LE(); //debug("Hide node %d", nodeRef); } } break; case cmdUnhide: { byte count = convFile->readByte(); for (byte k = 0; k < count; k++) { /*uint16 nodeRef = */convFile->readUint16LE(); //debug("Unhide node %d", nodeRef); } } break; case cmdMessage: //debug("Message"); convFile->skip(7); // TODO break; case cmdGoto: { convFile->skip(3); // unused? /*byte nodeRef = */convFile->readByte(); //debug("Goto %d", nodeRef); } break; case cmdAssign: { convFile->skip(3); // unused? /*uint16 value = */convFile->readUint16LE(); /*uint16 variable = */convFile->readUint16LE(); //debug("Variable %d = %d", variable, value); } break; default: error("Unknown conversation command %d", command); break; } } while (command != cmdNodeEnd && command != cmdDialogEnd); } } delete convFile; inFile.close(); // TODO: Still stuff to do warning("TODO GameConversations::get"); }
bool T7GFont::load(Common::SeekableReadStream &stream) { // Read the mapping of characters to glyphs if (stream.read(_mapChar2Glyph, 128) < 128) { error("Groovie::T7GFont: Couldn't read the character to glyph map"); return false; } // Calculate the number of glyphs byte numGlyphs = 0; for (int i = 0; i < 128; i++) if (_mapChar2Glyph[i] >= numGlyphs) numGlyphs = _mapChar2Glyph[i] + 1; // Read the glyph offsets uint16 *glyphOffsets = new uint16[numGlyphs]; for (int i = 0; i < numGlyphs; i++) glyphOffsets[i] = stream.readUint16LE(); if (stream.eos()) { error("Groovie::T7GFont: Couldn't read the glyph offsets"); delete[] glyphOffsets; return false; } // Allocate the glyph data delete[] _glyphs; _glyphs = new Glyph[numGlyphs]; // Ensure we're ready to read the first glyph. (Most versions don't // need it, but the russian one does. This fixes bug #3095031.) stream.seek(glyphOffsets[0]); // Read the glyphs _maxHeight = _maxWidth = 0; for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) { // Verify we're at the expected stream position if (stream.pos() != glyphOffsets[i]) { uint16 offset = glyphOffsets[i]; delete[] glyphOffsets; error("Groovie::T7GFont: Glyph %d starts at %d but the current " "offset is %d", i, offset, stream.pos()); return false; } // Read the glyph information Glyph *g = &_glyphs[i]; g->width = stream.readByte(); g->julia = stream.readByte(); // Read the pixels data into a dynamic array (we don't know its length) Common::Array<byte> data; data.reserve(300); byte b = stream.readByte(); while (!stream.eos() && (b != 0xFF)) { data.push_back(b); b = stream.readByte(); } // Verify the pixel data size assert (data.size() % g->width == 0); g->height = data.size() / g->width; // Copy the pixel data into the definitive static array g->pixels = new byte[data.size()]; memcpy(g->pixels, data.begin(), data.size()); // Update the max values if (g->width > _maxWidth) _maxWidth = g->width; if (g->height > _maxHeight) _maxHeight = g->height; } delete[] glyphOffsets; return true; }
bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) { destroy(); if (stream.readByte() != 0x0a) // ZSoft PCX return false; byte version = stream.readByte(); // 0 - 5 if (version > 5) return false; bool compressed = stream.readByte(); // encoding, 1 = run length encoding byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8 // Window uint16 xMin = stream.readUint16LE(); uint16 yMin = stream.readUint16LE(); uint16 xMax = stream.readUint16LE(); uint16 yMax = stream.readUint16LE(); uint16 width = xMax - xMin + 1; uint16 height = yMax - yMin + 1; if (xMax < xMin || yMax < yMin) { warning("Invalid PCX image dimensions"); return false; } stream.skip(4); // HDpi, VDpi // Read the EGA palette (colormap) _palette = new byte[16 * 3]; for (uint16 i = 0; i < 16; i++) { _palette[i * 3 + 0] = stream.readByte(); _palette[i * 3 + 1] = stream.readByte(); _palette[i * 3 + 2] = stream.readByte(); } if (stream.readByte() != 0) // reserved, should be set to 0 return false; byte nPlanes = stream.readByte(); uint16 bytesPerLine = stream.readUint16LE(); uint16 bytesPerscanLine = nPlanes * bytesPerLine; if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) { warning("PCX data is corrupted"); return false; } stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler _surface = new Graphics::Surface(); byte *scanLine = new byte[bytesPerscanLine]; byte *dst; int x, y; if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); _surface->create(width, height, format); dst = (byte *)_surface->getPixels(); _paletteColorCount = 0; for (y = 0; y < height; y++) { decodeRLE(stream, scanLine, bytesPerscanLine, compressed); for (x = 0; x < width; x++) { byte b = scanLine[x]; byte g = scanLine[x + bytesPerLine]; byte r = scanLine[x + (bytesPerLine << 1)]; uint32 color = format.RGBToColor(r, g, b); *((uint32 *)dst) = color; dst += format.bytesPerPixel; } } } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); dst = (byte *)_surface->getPixels(); _paletteColorCount = 16; for (y = 0; y < height; y++, dst += _surface->pitch) { decodeRLE(stream, scanLine, bytesPerscanLine, compressed); memcpy(dst, scanLine, width); } if (version == 5) { if (stream.readByte() != 12) { warning("Expected a palette after the PCX image data"); delete[] scanLine; return false; } // Read the VGA palette delete[] _palette; _palette = new byte[256 * 3]; for (uint16 i = 0; i < 256; i++) { _palette[i * 3 + 0] = stream.readByte(); _palette[i * 3 + 1] = stream.readByte(); _palette[i * 3 + 2] = stream.readByte(); } _paletteColorCount = 256; } } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); dst = (byte *)_surface->getPixels(); _paletteColorCount = 16; for (y = 0; y < height; y++, dst += _surface->pitch) { decodeRLE(stream, scanLine, bytesPerscanLine, compressed); for (x = 0; x < width; x++) { int m = 0x80 >> (x & 7), v = 0; for (int i = nPlanes - 1; i >= 0; i--) { v <<= 1; v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1; } dst[x] = v; } } } else {
void GameDatabaseV2::load(Common::SeekableReadStream &sourceS) { int16 version = sourceS.readUint16LE(); // Manhole:NE, Rodney's Funscreen and LGOP2 are version 54 // The earlier EGA version of Manhole is version 40 if (version != 54 && version != 40) warning("Unknown database version, known versions are 54 and 40"); char header[6]; sourceS.read(header, 6); if (strncmp(header, "ADVSYS", 6)) warning ("Unexpected database header, expected ADVSYS"); uint32 textOffs = 0, objectsOffs = 0, objectsSize = 0, textSize; uint16 objectCount = 0, varObjectCount = 0; sourceS.readUint16LE(); // skip sub-version sourceS.skip(18); // skip program name if (version == 40) { sourceS.readUint16LE(); // skip unused objectCount = sourceS.readUint16LE(); _gameStateSize = sourceS.readUint16LE() * 2; objectsOffs = sourceS.readUint16LE() * 512; textOffs = sourceS.readUint16LE() * 512; _mainCodeObjectIndex = sourceS.readUint16LE(); varObjectCount = 0; // unused in V1 objectsSize = 0; // unused in V1 } else if (version == 54) { textOffs = sourceS.readUint16LE() * 512; objectCount = sourceS.readUint16LE(); varObjectCount = sourceS.readUint16LE(); _gameStateSize = sourceS.readUint16LE() * 2; sourceS.readUint16LE(); // unknown objectsOffs = sourceS.readUint16LE() * 512; sourceS.readUint16LE(); // unknown _mainCodeObjectIndex = sourceS.readUint16LE(); sourceS.readUint16LE(); // unknown objectsSize = sourceS.readUint32LE() * 2; } textSize = objectsOffs - textOffs; debug(0, "textOffs = %08X; textSize = %08X; objectCount = %d; varObjectCount = %d; gameStateSize = %d; objectsOffs = %08X; objectsSize = %d; _mainCodeObjectIndex = %04X\n", textOffs, textSize, objectCount, varObjectCount, _gameStateSize, objectsOffs, objectsSize, _mainCodeObjectIndex); _gameState = new byte[_gameStateSize + 2]; memset(_gameState, 0, _gameStateSize + 2); setVar(1, objectCount); sourceS.seek(textOffs); _gameText = new char[textSize]; sourceS.read(_gameText, textSize); // "Decrypt" the text data for (uint32 i = 0; i < textSize; i++) _gameText[i] += 0x1E; sourceS.seek(objectsOffs); if (version == 40) { // Initialize the object array for (uint32 i = 0; i < objectCount; i++) _objects.push_back(NULL); // Read two "sections" of objects // It seems the first section is data while the second one is code. // The interpreter however doesn't care which section the objects come from. for (int section = 0; section < 2; section++) { while (!sourceS.eos()) { int16 objIndex = sourceS.readUint16LE(); debug("objIndex = %04X; section = %d", objIndex, section); if (objIndex == 0) break; Object *obj = new ObjectV1(); obj->load(sourceS); _objects[objIndex - 1] = obj; } } } else if (version == 54) { for (uint32 i = 0; i < objectCount; i++) { Object *obj = new ObjectV2(); int objSize = obj->load(sourceS); // Objects are aligned on 2-byte-boundaries, skip unused bytes sourceS.skip(objSize % 2); _objects.push_back(obj); } } }
bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) { if (!tga.seek(0)) { warning("Failed reading TGA-file"); return false; } // TGAs have an optional "id" string in the header uint32 idLength = tga.readByte(); // Number of colors in the color map / palette int hasColorMap = tga.readByte(); // Image type. See header for numeric constants imageType = tga.readByte(); switch (imageType) { case TYPE_CMAP: case TYPE_TRUECOLOR: case TYPE_BW: case TYPE_RLE_CMAP: case TYPE_RLE_TRUECOLOR: case TYPE_RLE_BW: break; default: warning("Unsupported image type: %d", imageType); return false; } // Color map specifications if (hasColorMap == 0) { tga.skip(5); } else { _colorMapOrigin = tga.readUint16LE(); _colorMapLength = tga.readUint16LE(); _colorMapEntryLength = tga.readByte(); } // Origin-defintions tga.skip(2 + 2); // Image dimensions _surface.w = tga.readUint16LE(); _surface.h = tga.readUint16LE(); // Bits per pixel pixelDepth = tga.readByte(); _surface.format.bytesPerPixel = pixelDepth / 8; // Image descriptor byte imgDesc = tga.readByte(); int attributeBits = imgDesc & 0x0F; assert((imgDesc & 0x10) == 0); _originTop = (imgDesc & 0x20); // Interleaving is not handled at this point //int interleave = (imgDesc & 0xC); if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) { if (pixelDepth == 8) { _format = Graphics::PixelFormat::createFormatCLUT8(); } else { warning("Unsupported index-depth: %d", pixelDepth); return false; } } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) { if (pixelDepth == 24) { _format = Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0); } else if (pixelDepth == 32) { // HACK: According to the spec, attributeBits should determine the amount // of alpha-bits, however, as the game files that use this decoder seems // to ignore that fact, we force the amount to 8 for 32bpp files for now. _format = Graphics::PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24); } else if (pixelDepth == 16) { // 16bpp TGA is ARGB1555 _format = Graphics::PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15); } else { warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); return false; } } else if (imageType == TYPE_BW || imageType == TYPE_RLE_BW) { if (pixelDepth == 8) { _format = Graphics::PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0); } else { warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); return false; } } else { warning("Unsupported image type: %d", imageType); return false; } // Skip the id string tga.skip(idLength); if (hasColorMap) { return readColorMap(tga, imageType, pixelDepth); } return true; }
void SEQFile::load(Common::SeekableReadStream &seq) { const uint16 decCount = (uint16)seq.readByte() + 1; const uint16 aniCount = (uint16)seq.readByte() + 1; // Load backgrounds _backgrounds.reserve(decCount); for (uint i = 0; i < decCount; i++) { const Common::String dec = Util::readString(seq, 13); if (!_vm->_dataIO->hasFile(dec)) { warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); return; } _backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); } // Load animations _animations.reserve(aniCount); for (uint i = 0; i < aniCount; i++) { const Common::String ani = Util::readString(seq, 13); if (!_vm->_dataIO->hasFile(ani)) { warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); return; } _animations.push_back(new ANIFile(_vm, ani)); } _frameRate = seq.readUint16LE(); // Load background change keys const uint16 bgKeyCount = seq.readUint16LE(); _bgKeys.resize(bgKeyCount); for (uint16 i = 0; i < bgKeyCount; i++) { const uint16 frame = seq.readUint16LE(); const uint16 index = seq.readUint16LE(); _bgKeys[i].frame = frame; _bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; } // Load animation keys for all 4 objects for (uint i = 0; i < kObjectCount; i++) { const uint16 animKeyCount = seq.readUint16LE(); _animKeys.reserve(_animKeys.size() + animKeyCount); for (uint16 j = 0; j < animKeyCount; j++) { _animKeys.push_back(AnimationKey()); const uint16 frame = seq.readUint16LE(); const uint16 index = seq.readUint16LE(); uint16 animation; const ANIFile *ani = findANI(index, animation); _animKeys.back().object = i; _animKeys.back().frame = frame; _animKeys.back().ani = ani; _animKeys.back().animation = animation; _animKeys.back().x = seq.readSint16LE(); _animKeys.back().y = seq.readSint16LE(); _animKeys.back().order = seq.readSint16LE(); } } }
void Insane::iactScene6(byte *renderBitmap, int32 codecparam, int32 setupsan12, int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags, int16 par1, int16 par2, int16 par3, int16 par4) { int16 par5; switch (par1) { case 7: par5 = b.readUint16LE(); if (par4 != 3) break; if (par5 >= _actor[0].x) break; _actor[0].x = par5; break; case 2: case 4: par5 = b.readUint16LE(); switch (par3) { case 1: if (par4 == 1) { if (readArray(6)) setBit(par5); else clearBit(par5); } else { if (readArray(6)) clearBit(par5); else setBit(par5); } break; case 2: if (readArray(5)) clearBit(par5); else setBit(par5); break; } break; case 6: switch (par2) { case 38: smlayer_drawSomething(renderBitmap, codecparam, 270-19, 20-13, 3, _smush_icons2Nut, 10, 0, 0); _roadBranch = true; _iactSceneId = par4; break; case 7: if (readArray(4) != 0) return; _roadStop = true; smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, //QW _smush_icons2Nut, 8, 0, 0); break; case 8: if (readArray(4) == 0 || readArray(6) == 0) return; writeArray(1, _posBrokenTruck); writeArray(3, _posVista); smush_setToFinish(); break; case 25: if (readArray(5) == 0) return; _carIsBroken = true; smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, //QW _smush_icons2Nut, 8, 0, 0); break; case 11: smlayer_drawSomething(renderBitmap, codecparam, 50-19, 20-13, 3, _smush_icons2Nut, 9, 0, 0); _roadBranch = true; _iactSceneId = par4; break; } break; } }
void Insane::iactScene1(byte *renderBitmap, int32 codecparam, int32 setupsan12, int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags, int16 par1, int16 par2, int16 par3, int16 par4) { int16 par5, par6, par7, par9, par11, par13, tmp; switch (par1) { case 2: // PATCH if (par3 != 1) break; par5 = b.readUint16LE(); // si if (_actor[0].field_8 == 112) { setBit(par5); break; } if (_approachAnim == -1) { chooseEnemy(); //PATCH _approachAnim = _enemy[_currEnemy].apprAnim; } if (_approachAnim == par4) clearBit(par5); else setBit(par5); break; case 3: if (par3 == 1) { setBit(b.readUint16LE()); _approachAnim = -1; } break; case 4: if (par3 == 1 && (_approachAnim < 0 || _approachAnim > 4)) setBit(b.readUint16LE()); break; case 5: if (par2 != 13) break; tmp = b.readUint16LE(); // +8 tmp = b.readUint16LE(); // +10 par7 = b.readUint16LE(); // +12 dx tmp = b.readUint16LE(); // +14 par9 = b.readUint16LE(); // +16 bx tmp = b.readUint16LE(); // +18 par11 = b.readUint16LE(); // +20 cx tmp = b.readUint16LE(); // +22 par13 = b.readUint16LE(); // +24 ax if (par13 > _actor[0].x || par11 < _actor[0].x) { _tiresRustle = true; _actor[0].x1 = -_actor[0].x1; _actor[0].damage++; // PATCH } if (par9 < _actor[0].x || par7 > _actor[0].x) { _tiresRustle = true; _actor[0].damage += 4; // PATCH } break; case 6: switch (par2) { case 38: smlayer_drawSomething(renderBitmap, codecparam, 50-19, 20-13, 3, _smush_iconsNut, 7, 0, 0); _roadBranch = true; _iactSceneId = par4; break; case 25: _roadBumps = true; _actor[0].y1 = -_actor[0].y1; break; case 11: if (_approachAnim >= 1 && _approachAnim <= 4 && !_needSceneSwitch) queueSceneSwitch(13, _smush_minefiteFlu, "minefite.san", 64, 0, _continueFrame1, 1300); break; case 9: par5 = b.readUint16LE(); // si par6 = b.readUint16LE(); // bx smlayer_setFluPalette(_smush_roadrsh3Rip, 0); if (par5 == par6 - 1) smlayer_setFluPalette(_smush_roadrashRip, 0); } break; case 7: switch (par4) { case 1: _actor[0].x -= (b.readUint16LE() - 160) / 10; break; case 2: par5 = b.readUint16LE(); if (par5 - 8 > _actor[0].x || par5 + 8 < _actor[0].x) { if (smlayer_isSoundRunning(86)) smlayer_stopSound(86); } else { if (!smlayer_isSoundRunning(86)) smlayer_startSfx(86); } break; } break; } if (_approachAnim < 0 || _approachAnim > 4) if (readArray(8)) { smlayer_drawSomething(renderBitmap, codecparam, 270-19, 20-18, 3, _smush_iconsNut, 20, 0, 0); _benHasGoggles = true; } }
bool Sprite::loadFromBMP(Common::SeekableReadStream &bmp) { discard(); if (!bmp.seek(0)) return false; uint32 fSize = bmp.size(); // 'BM' if (bmp.readUint16BE() != 0x424D) return false; // Size of image + reserved + reserved bmp.skip(8); uint32 bmpDataOffset = bmp.readUint32LE(); if (bmpDataOffset >= fSize) return false; // Header size if (bmp.readUint32LE() != 40) return false; int32 width = (int32) bmp.readUint32LE(); int32 height = (int32) bmp.readUint32LE(); // Sanity checks assert((width > 0) && (height > 0) && (width <= 0x7FFF) && (height <= 0x7FFF)); // Create surfaces create(width, height); // Number of color planes if (bmp.readUint16LE() != 1) return false; // Bits per pixel if (bmp.readUint16LE() != 8) return false; uint32 compression = bmp.readUint32LE(); if ((compression != 0) && (compression != 2)) return false; uint32 bmpDataSize = bmp.readUint32LE(); // Sprite's feet position _feetX = (int32) MIN<uint16>(ABS(((int16) bmp.readUint16LE())), width - 1); _feetY = (int32) MIN<uint16>(ABS(((int16) bmp.readUint16LE())), height - 1); // Default coordinates _defaultX = (int32) bmp.readUint16LE(); _defaultY = (int32) bmp.readUint16LE(); uint32 numPalColors = bmp.readUint32LE(); if (numPalColors == 0) numPalColors = 256; if (numPalColors > 256) numPalColors = 256; if (bmpDataOffset == 54) { // Image data begins right after the header => no palette numPalColors = 0; } // Important colors bmp.skip(4); loadPalette(bmp, numPalColors); if (!bmp.seek(bmpDataOffset)) return false; if (compression == 0) { if (!readBMPDataComp0(bmp, bmpDataSize)) return false; } else if (compression == 2) { if (!readBMPDataComp2(bmp, bmpDataSize)) return false; } createTransparencyMap(); convertToTrueColor(); return true; }
void ComposerEngine::loadLibrary(uint id) { if (getGameType() == GType_ComposerV1 && !_libraries.empty()) { // kill the previous page, starting with any scripts running on it for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) delete *i; _oldScripts.clear(); Library *library = &_libraries.front(); unloadLibrary(library->_id); } Common::String filename; if (getGameType() == GType_ComposerV1) { if (!id || _bookGroup.empty()) filename = getStringFromConfig("Common", "StartPage"); else filename = getStringFromConfig(_bookGroup, Common::String::format("%d", id)); filename = mangleFilename(filename); // bookGroup is the basename of the path. // TODO: tidy this up. _bookGroup.clear(); for (uint i = 0; i < filename.size(); i++) { if (filename[i] == '~' || filename[i] == '/' || filename[i] == ':') continue; for (uint j = 0; j < filename.size(); j++) { if (filename[j] == '/') { _bookGroup.clear(); continue; } if (filename[j] == '.') break; _bookGroup += filename[j]; } break; } } else { if (!id) id = atoi(getStringFromConfig("Common", "StartUp").c_str()); filename = getFilename("Libs", id); } Library library; library._id = id; library._archive = new ComposerArchive(); if (!library._archive->openFile(filename)) error("failed to open '%s'", filename.c_str()); _libraries.push_front(library); Library &newLib = _libraries.front(); Common::Array<uint16> buttonResources = library._archive->getResourceIDList(ID_BUTN); for (uint i = 0; i < buttonResources.size(); i++) { uint16 buttonId = buttonResources[i]; Common::SeekableReadStream *stream = library._archive->getResource(ID_BUTN, buttonId); Button button(stream, buttonId, getGameType()); bool inserted = false; for (Common::List<Button>::iterator b = newLib._buttons.begin(); b != newLib._buttons.end(); b++) { if (button._zorder < b->_zorder) continue; newLib._buttons.insert(b, button); inserted = true; break; } if (!inserted) newLib._buttons.push_back(button); } Common::Array<uint16> ambientResources = library._archive->getResourceIDList(ID_AMBI); for (uint i = 0; i < ambientResources.size(); i++) { Common::SeekableReadStream *stream = library._archive->getResource(ID_AMBI, ambientResources[i]); Button button(stream); newLib._buttons.insert(newLib._buttons.begin(), button); } Common::Array<uint16> accelResources = library._archive->getResourceIDList(ID_ACEL); for (uint i = 0; i < accelResources.size(); i++) { Common::SeekableReadStream *stream = library._archive->getResource(ID_ACEL, accelResources[i]); KeyboardHandler handler; handler.keyId = stream->readUint16LE(); handler.modifierId = stream->readUint16LE(); handler.scriptId = stream->readUint16LE(); newLib._keyboardHandlers.push_back(handler); } Common::Array<uint16> randResources = library._archive->getResourceIDList(ID_RAND); for (uint i = 0; i < randResources.size(); i++) { Common::SeekableReadStream *stream = library._archive->getResource(ID_RAND, randResources[i]); Common::Array<RandomEvent> &events = _randomEvents[randResources[i]]; uint16 count = stream->readUint16LE(); for (uint j = 0; j < count; j++) { RandomEvent random; random.scriptId = stream->readUint16LE(); random.weight = stream->readUint16LE(); events.push_back(random); } delete stream; } // add background sprite, if it exists if (hasResource(ID_BMAP, 1000)) setBackground(1000); // TODO: better CTBL logic loadCTBL(1000, 100); // Run the startup script. runScript(1000, 0, 0, 0); _mouseEnabled = true; onMouseMove(_lastMousePos); runEvent(kEventLoad, id, 0, 0); }
void TXB::readHeader(Common::SeekableReadStream &txb, bool &needDeSwizzle) { // Number of bytes for the pixel data in one full image uint32 dataSize = txb.readUint32LE(); _dataSize = dataSize; txb.skip(4); // Some float // Image dimensions uint32 width = txb.readUint16LE(); uint32 height = txb.readUint16LE(); // How's the pixel data encoded? byte encoding = txb.readByte(); // Number of mip maps in the image byte mipMapCount = txb.readByte(); txb.skip(2); // Unknown (Always 0x0101 on 0x0A and 0x0C types, 0x0100 on 0x09?) txb.skip(4); // Some float txb.skip(108); // Reserved needDeSwizzle = false; uint32 minDataSize, mipMapSize; if (encoding == kEncodingBGRA) { // Raw BGRA needDeSwizzle = true; _compressed = false; _hasAlpha = true; _format = kPixelFormatBGRA; _formatRaw = kPixelFormatRGBA8; _dataType = kPixelDataType8; minDataSize = 4; mipMapSize = width * height * 4; } else if (encoding == kEncodingDXT1) { // S3TC DXT1 _compressed = true; _hasAlpha = false; _format = kPixelFormatBGR; _formatRaw = kPixelFormatDXT1; _dataType = kPixelDataType8; minDataSize = 8; mipMapSize = width * height / 2; } else if (encoding == kEncodingDXT5) { // S3TC DXT5 _compressed = true; _hasAlpha = true; _format = kPixelFormatBGRA; _formatRaw = kPixelFormatDXT5; _dataType = kPixelDataType8; minDataSize = 16; mipMapSize = width * height; } else if (encoding == 0x09) // TODO: This seems to be some compression with 8bit per pixel. No min // data size; 2*2 and 1*1 mipmaps seem to be just that big. // Image data doesn't seem to be simple grayscale, paletted, // RGB2222 or RGB332 data either. throw Common::Exception("Unsupported TXB encoding 0x09"); else throw Common::Exception("Unknown TXB encoding 0x%02X (%dx%d, %d, %d)", encoding, width, height, mipMapCount, dataSize); _mipMaps.reserve(mipMapCount); for (uint32 i = 0; i < mipMapCount; i++) { MipMap *mipMap = new MipMap; mipMap->width = MAX<uint32>(width, 1); mipMap->height = MAX<uint32>(height, 1); if (((width < 4) || (height < 4)) && (width != height)) // Invalid mipmap dimensions break; mipMap->size = MAX<uint32>(mipMapSize, minDataSize); mipMap->data = 0; if (dataSize < mipMap->size) { // Wouldn't fit delete mipMap; break; } dataSize -= mipMap->size; _mipMaps.push_back(mipMap); width >>= 1; height >>= 1; mipMapSize >>= 2; if ((width < 1) && (height < 1)) break; } }
void MidiMusic::load(Common::SeekableReadStream &stream) { _musicId = stream.readUint32LE(); _looping = stream.readUint16LE() != 0; stream.skip(2 + 32 + 4); // Skip unused/unknown values debug(1, "MidiMusic::load() _musicId: %08X; _looping: %d", _musicId, _looping); }
void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) { char buffer1[80]; const char *sceneName; // TODO: Initialise spriteSet / xp_list if (sceneNumber > 0) { sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT"); } else { strcpy(buffer1, "*"); strcat(buffer1, resName); sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form' } Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName); MadsPack sceneInfo(rawStream); // Chunk 0: // Basic scene info Common::SeekableReadStream *stream = sceneInfo.getItemStream(0); if (_vm->getGameType() == GType_RexNebular) { int resSceneId = stream->readUint16LE(); assert(resSceneId == sceneNumber); } else { char roomFilename[10]; char roomFilenameExpected[10]; sprintf(roomFilenameExpected, "*RM%d", sceneNumber); stream->read(roomFilename, 6); roomFilename[6] = 0; assert(!strcmp(roomFilename, roomFilenameExpected)); } // TODO: The following is wrong for Phantom/Dragon _artFileNum = stream->readUint16LE(); _depthStyle = stream->readUint16LE(); _width = stream->readUint16LE(); _height = stream->readUint16LE(); stream->skip(24); int nodeCount = stream->readUint16LE(); _yBandsEnd = stream->readUint16LE(); _yBandsStart = stream->readUint16LE(); _maxScale = stream->readSint16LE(); _minScale = stream->readSint16LE(); for (int i = 0; i < DEPTH_BANDS_SIZE; ++i) _depthBands[i] = stream->readUint16LE(); stream->skip(2); // Load in any scene objects for (int i = 0; i < nodeCount; ++i) { SceneNode rec; rec.load(stream); _nodes.push_back(rec); } for (int i = 0; i < 20 - nodeCount; ++i) stream->skip(48); // Add two extra nodes in that will be used for player movement for (int i = 0; i < 2; ++i) { SceneNode rec; _nodes.push_back(rec); } int setCount = stream->readUint16LE(); stream->readUint16LE(); for (int i = 0; i < setCount; ++i) { char buffer2[64]; Common::String s(buffer2, 64); _setNames.push_back(s); } delete stream; // Initialise a copy of the surfaces if they weren't provided bool dsFlag = false, ssFlag = false; if (!surface) { surface = new M4Surface(_width, _height); ssFlag = true; } else if ((_width != surface->width()) || (_height != surface->height())) surface->setSize(_width, _height); if (!depthSurface) { depthSurface = new M4Surface(_width, _height); dsFlag = true; } else if ((_width != depthSurface->width()) || (_height != depthSurface->height())) depthSurface->setSize(_width, _height); // For Rex Nebular, read in the scene's compressed walk surface information if (_vm->getGameType() == GType_RexNebular) { assert(depthSurface); stream = sceneInfo.getItemStream(1); byte *walkData = (byte *)malloc(stream->size()); stream->read(walkData, stream->size()); // For Rex Nebular, the walk areas are part of the scene info byte *destP = depthSurface->getBasePtr(0, 0); const byte *srcP = walkData; byte runLength; // Run length encoded depth data while ((runLength = *srcP++) != 0) { if (_depthStyle == 2) { // 2-bit depth pixels byte byteVal = *srcP++; for (int byteCtr = 0; byteCtr < runLength; ++byteCtr) { byte v = byteVal; for (int bitCtr = 0; bitCtr < 4; ++bitCtr, v >>= 2) *destP++ = (((v & 1) + 1) << 3) - 1; } } else {
void FileIdent::load(Common::SeekableReadStream &s) { _fileNum = s.readSint16LE(); _subfile = s.readUint16LE(); }
ASFStream::Packet *ASFStream::readPacket() { if (_curPacket == _packetCount) throw Common::Exception("ASFStream::readPacket(): Reading too many packets"); uint32 packetStartPos = _stream->pos(); // Read a single ASF packet if (_stream->readByte() != 0x82) throw Common::Exception("ASFStream::readPacket(): Missing packet header"); if (_stream->readUint16LE() != 0) throw Common::Exception("ASFStream::readPacket(): Unknown is not zero"); Packet *packet = new Packet(); packet->flags = _stream->readByte(); packet->segmentType = _stream->readByte(); packet->packetSize = (packet->flags & 0x40) ? _stream->readUint16LE() : 0; uint16 paddingSize = 0; if (packet->flags & 0x10) paddingSize = _stream->readUint16LE(); else if (packet->flags & 0x08) paddingSize = _stream->readByte(); packet->sendTime = _stream->readUint32LE(); packet->duration = _stream->readUint16LE(); byte segmentCount = (packet->flags & 0x01) ? _stream->readByte() : 1; packet->segments.resize(segmentCount & 0x3F); for (uint32 i = 0; i < packet->segments.size(); i++) { Packet::Segment &segment = packet->segments[i]; segment.streamID = _stream->readByte(); segment.sequenceNumber = _stream->readByte(); segment.isKeyframe = (segment.streamID & 0x80) != 0; segment.streamID &= 0x7F; uint32 fragmentOffset = 0; if (packet->segmentType == 0x55) fragmentOffset = _stream->readByte(); else if (packet->segmentType == 0x59) fragmentOffset = _stream->readUint16LE(); else if (packet->segmentType == 0x5D) fragmentOffset = _stream->readUint32LE(); else throw Common::Exception("ASFStream::readPacket(): Unknown packet segment type 0x%02x", packet->segmentType); byte flags = _stream->readByte(); if (flags == 1) { //uint32 objectStartTime = fragmentOffset; // reused purpose _stream->readByte(); // unknown uint32 dataLength = (packet->segments.size() == 1) ? (_maxPacketSize - (_stream->pos() - packetStartPos) - paddingSize) : _stream->readUint16LE(); uint32 startObjectPos = _stream->pos(); while ((uint32)_stream->pos() < dataLength + startObjectPos) segment.data.push_back(_stream->readStream(_stream->readByte())); } else if (flags == 8) { /* uint32 objectLength = */ _stream->readUint32LE(); /* uint32 objectStartTime = */ _stream->readUint32LE(); uint32 dataLength = 0; if (packet->segments.size() == 1) dataLength = _maxPacketSize - (_stream->pos() - packetStartPos) - fragmentOffset - paddingSize; else if (segmentCount & 0x40) dataLength = _stream->readByte(); else dataLength = _stream->readUint16LE(); _stream->skip(fragmentOffset); segment.data.push_back(_stream->readStream(dataLength)); } else throw Common::Exception("ASFStream::readPacket(): Unknown packet flags 0x%02x", flags); } // Skip any padding _stream->skip(paddingSize); // We just read a packet _curPacket++; if ((uint32)_stream->pos() != packetStartPos + _maxPacketSize) throw Common::Exception("ASFStream::readPacket(): Mismatching packet pos: %d (should be %d)", _stream->pos(), _maxPacketSize + packetStartPos); return packet; }
bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry &dirEntry) { // TODO: PE libraries (If it's used anywhere by a ScummVM game) Common::NEResources exe; if (!exe.loadFromEXE(fileName)) return false; // Let's pull out the font directory Common::SeekableReadStream *fontDirectory = exe.getResource(Common::kNEFontDir, Common::String("FONTDIR")); if (!fontDirectory) { warning("No font directory in '%s'", fileName.c_str()); return false; } uint16 numFonts = fontDirectory->readUint16LE(); // Probably not possible, so this is really a sanity check if (numFonts == 0) { warning("No fonts in '%s'", fileName.c_str()); return false; } // Scour the directory for our matching name int fontId = -1; for (uint16 i = 0; i < numFonts; i++) { uint16 id = fontDirectory->readUint16LE(); if (dirEntry.faceName.empty()) { // Use the first name when empty fontId = id; break; } WinFontDirEntry entry = readDirEntry(*fontDirectory); if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) { // Match! fontId = id; break; } } delete fontDirectory; // Couldn't match the face name if (fontId < 0) { warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str()); return false; } // Actually go get our font now... Common::SeekableReadStream *fontStream = exe.getResource(Common::kNEFont, fontId); if (!fontStream) { warning("Could not find font %d in %s", fontId, fileName.c_str()); return false; } bool ok = loadFromFNT(*fontStream); delete fontStream; return ok; }
void Map_v1::loadObjects(Common::SeekableReadStream &data, uint32 objsPos) { Goblin::Gob_State *pState; uint32 tmpStateData[40 * 6]; uint32 tmpPos; _vm->_goblin->_objCount = data.readUint16LE(); for (int i = 0; i < _vm->_goblin->_objCount; i++) { _vm->_goblin->_objects[i] = new Goblin::Gob_Object; memset(_vm->_goblin->_objects[i], 0, sizeof(Goblin::Gob_Object)); tmpPos = data.pos(); data.seek(objsPos); _vm->_goblin->_objects[i]->xPos = data.readUint16LE(); _vm->_goblin->_objects[i]->yPos = data.readUint16LE(); _vm->_goblin->_objects[i]->order = data.readUint16LE(); _vm->_goblin->_objects[i]->state = data.readUint16LE(); objsPos = data.pos(); data.seek(tmpPos); _vm->_goblin->_objects[i]->stateMach = new Goblin::Gob_StateLine[40]; for (int state = 0; state < 40; ++state) { for (int col = 0; col < 6; ++col) { _vm->_goblin->_objects[i]->stateMach[state][col] = 0; tmpStateData[state * 6 + col] = data.readUint32LE(); } } data.skip(160); _vm->_goblin->_objects[i]->multObjIndex = data.readByte(); data.skip(1); _vm->_goblin->_objects[i]->realStateMach = _vm->_goblin->_objects[i]->stateMach; for (int state = 0; state < 40; state++) { for (int col = 0; col < 6; col++) { if (tmpStateData[state * 6 + col] == 0) { _vm->_goblin->_objects[i]->stateMach[state][col] = 0; continue; } Goblin::Gob_State *tmpState = new Goblin::Gob_State; memset(tmpState, 0, sizeof(Goblin::Gob_State)); _vm->_goblin->_objects[i]->stateMach[state][col] = tmpState; tmpState->animation = data.readUint16LE(); tmpState->layer = data.readUint16LE(); data.skip(8); tmpState->unk0 = data.readUint16LE(); tmpState->unk1 = data.readUint16LE(); data.skip(2); if (data.readUint32LE() == 0) { data.skip(2); tmpState->sndItem = -1; } else tmpState->sndItem = data.readUint16LE(); tmpState->freq = data.readUint16LE(); tmpState->repCount = data.readUint16LE(); tmpState->sndFrame = data.readUint16LE(); } } } _vm->_goblin->_objects[10] = new Goblin::Gob_Object; memset(_vm->_goblin->_objects[10], 0, sizeof(Goblin::Gob_Object)); _vm->_goblin->_objects[10]->stateMach = new Goblin::Gob_StateLine[40]; for (int state = 0; state < 40; ++state) for (int col = 0; col < 6; ++col) _vm->_goblin->_objects[10]->stateMach[state][col] = 0; pState = new Goblin::Gob_State; memset(pState, 0, sizeof(Goblin::Gob_State)); _vm->_goblin->_objects[10]->stateMach[0][0] = pState; pState->animation = 9; pState->layer = 27; pState->sndItem = -1; _vm->_goblin->placeObject(_vm->_goblin->_objects[10], 1, 0, 0, 0, 0); _vm->_goblin->_objects[10]->realStateMach = _vm->_goblin->_objects[10]->stateMach; _vm->_goblin->_objects[10]->type = 1; _vm->_goblin->_objects[10]->unk14 = 1; }