/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/system.h" #include "common/config-manager.h" #include "common/file.h" #include "common/memstream.h" #include "common/substream.h" #include "engines/util.h" #include "graphics/primitives.h" #include "graphics/macgui/macfontmanager.h" #include "graphics/macgui/macwindowmanager.h" #include "image/bmp.h" #include "director/director.h" #include "director/cast.h" #include "director/images.h" #include "director/score.h" #include "director/frame.h" #include "director/sound.h" #include "director/sprite.h" #include "director/stxt.h" #include "director/util.h" #include "director/lingo/lingo.h" namespace Director { const char *scriptTypes[] = { "MovieScript", "SpriteScript", "FrameScript", "CastScript", "GlobalScript", "ScoreScript" }; const char *scriptType2str(ScriptType scr) { if (scr < 0) return "NoneScript"; if (scr > kMaxScriptType) return ""; return scriptTypes[scr]; } Score::Score(DirectorEngine *vm) { _vm = vm; _surface = nullptr; _trailSurface = nullptr; _backSurface = nullptr; _lingo = _vm->getLingo(); _soundManager = _vm->getSoundManager(); _currentMouseDownSpriteId = 0; _mouseIsDown = false; // FIXME: TODO: Check whether the original truely does it if (_vm->getVersion() <= 3) { _lingo->executeScript(kMovieScript, 0, 0); } _movieScriptCount = 0; _labels = NULL; _font = NULL; _versionMinor = _versionMajor = 0; _currentFrameRate = 20; _castArrayStart = _castArrayEnd = 0; _currentFrame = 0; _nextFrame = 0; _currentLabel = 0; _nextFrameTime = 0; _flags = 0; _stopPlay = false; _stageColor = 0; _castIDoffset = 0; _movieArchive = nullptr; _loadedStxts = nullptr; _loadedCast = nullptr; } void Score::setArchive(Archive *archive) { _movieArchive = archive; if (archive->hasResource(MKTAG('M', 'C', 'N', 'M'), 0)) { _macName = archive->getName(MKTAG('M', 'C', 'N', 'M'), 0).c_str(); } else { _macName = archive->getFileName(); } // Frame Labels if (archive->hasResource(MKTAG('V', 'W', 'L', 'B'), -1)) { loadLabels(*archive->getFirstResource(MKTAG('V', 'W', 'L', 'B'))); } } void Score::loadArchive() { Common::Array clutList = _movieArchive->getResourceIDList(MKTAG('C', 'L', 'U', 'T')); if (clutList.size() > 1) warning("More than one palette was found (%d)", clutList.size()); if (clutList.size() == 0) { warning("CLUT resource not found, using default Mac palette"); _vm->setPalette(-1); } else { Common::SeekableSubReadStreamEndian *pal = _movieArchive->getResource(MKTAG('C', 'L', 'U', 'T'), clutList[0]); debugC(2, kDebugLoading, "****** Loading Palette CLUT"); loadPalette(*pal); } // Font Directory if (_movieArchive->hasResource(MKTAG('F', 'O', 'N', 'D'), -1)) { debug("STUB: Unhandled FOND resource"); } // Score assert(_movieArchive->hasResource(MKTAG('V', 'W', 'S', 'C'), -1)); loadFrames(*_movieArchive->getFirstResource(MKTAG('V', 'W', 'S', 'C'))); // Configuration Information if (_movieArchive->hasResource(MKTAG('V', 'W', 'C', 'F'), -1)) { loadConfig(*_movieArchive->getFirstResource(MKTAG('V', 'W', 'C', 'F'))); } else { // TODO: Source this from somewhere! _movieRect = Common::Rect(0, 0, 640, 480); _stageColor = 1; } // Cast Information Array if (_movieArchive->hasResource(MKTAG('V', 'W', 'C', 'R'), -1)) { _castIDoffset = _movieArchive->getResourceIDList(MKTAG('V', 'W', 'C', 'R'))[0]; loadCastDataVWCR(*_movieArchive->getResource(MKTAG('V', 'W', 'C', 'R'), _castIDoffset)); } // Action list if (_movieArchive->hasResource(MKTAG('V', 'W', 'A', 'C'), -1)) { loadActions(*_movieArchive->getFirstResource(MKTAG('V', 'W', 'A', 'C'))); } // File Info if (_movieArchive->hasResource(MKTAG('V', 'W', 'F', 'I'), -1)) { loadFileInfo(*_movieArchive->getFirstResource(MKTAG('V', 'W', 'F', 'I'))); } // Font Mapping if (_movieArchive->hasResource(MKTAG('V', 'W', 'F', 'M'), -1)) { _vm->_wm->_fontMan->clearFontMapping(); loadFontMap(*_movieArchive->getFirstResource(MKTAG('V', 'W', 'F', 'M'))); } // Pattern Tiles if (_movieArchive->hasResource(MKTAG('V', 'W', 'T', 'L'), -1)) { debug("STUB: Unhandled VWTL resource."); } // Time code if (_movieArchive->hasResource(MKTAG('V', 'W', 't', 'c'), -1)) { debug("STUB: Unhandled VWtc resource"); } // External sound files if (_movieArchive->hasResource(MKTAG('S', 'T', 'R', ' '), -1)) { debug("STUB: Unhandled 'STR ' resource"); } // Try to load script context if (_vm->getVersion() >= 4) { Common::Array lctx = _movieArchive->getResourceIDList(MKTAG('L','c','t','x')); if (lctx.size() > 0) { debugC(2, kDebugLoading, "****** Loading %d Lctx resources", lctx.size()); for (Common::Array::iterator iterator = lctx.begin(); iterator != lctx.end(); ++iterator) { loadLingoContext(*_movieArchive->getResource(MKTAG('L','c','t','x'), *iterator)); } } } // Try to load script name lists if (_vm->getVersion() >= 4) { Common::Array lnam = _movieArchive->getResourceIDList(MKTAG('L','n','a','m')); if (lnam.size() > 0) { debugC(2, kDebugLoading, "****** Loading %d Lnam resources", lnam.size()); for (Common::Array::iterator iterator = lnam.begin(); iterator != lnam.end(); ++iterator) { loadLingoNames(*_movieArchive->getResource(MKTAG('L','n','a','m'), *iterator)); } } } Common::Array vwci = _movieArchive->getResourceIDList(MKTAG('V', 'W', 'C', 'I')); if (vwci.size() > 0) { debugC(2, kDebugLoading, "****** Loading %d CastInfos VWCI", vwci.size()); for (Common::Array::iterator iterator = vwci.begin(); iterator != vwci.end(); ++iterator) loadCastInfo(*_movieArchive->getResource(MKTAG('V', 'W', 'C', 'I'), *iterator), *iterator); } Common::Array cast = _movieArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')); if (!_loadedCast) _loadedCast = new Common::HashMap(); if (cast.size() > 0) { debugC(2, kDebugLoading, "****** Loading %d CASt resources", cast.size()); for (Common::Array::iterator iterator = cast.begin(); iterator != cast.end(); ++iterator) { Common::SeekableSubReadStreamEndian *stream = _movieArchive->getResource(MKTAG('C', 'A', 'S', 't'), *iterator); Resource res = _movieArchive->getResourceDetail(MKTAG('C', 'A', 'S', 't'), *iterator); loadCastData(*stream, *iterator, &res); } } // PICT resources if (_movieArchive->hasResource(MKTAG('P', 'I', 'C', 'T'), -1)) { debug("STUB: Unhandled 'PICT' resource"); } // Sound resources if (_movieArchive->hasResource(MKTAG('s', 'n', 'd', ' '), -1)) { debug("STUB: Unhandled 'snd ' resource"); } // Film Loop resources if (_movieArchive->hasResource(MKTAG('S', 'C', 'V', 'W'), -1)) { debug("STUB: Unhandled 'SCVW' resource"); } setSpriteCasts(); loadSpriteImages(false); // Now process STXTs Common::Array stxt = _movieArchive->getResourceIDList(MKTAG('S','T','X','T')); debugC(2, kDebugLoading, "****** Loading %d STXT resources", stxt.size()); _loadedStxts = new Common::HashMap(); for (Common::Array::iterator iterator = stxt.begin(); iterator != stxt.end(); ++iterator) { _loadedStxts->setVal(*iterator, new Stxt(*_movieArchive->getResource(MKTAG('S','T','X','T'), *iterator))); // Try to load movie script, it starts with a comment if (_vm->getVersion() <= 3) { loadScriptText(*_movieArchive->getResource(MKTAG('S','T','X','T'), *iterator)); } } copyCastStxts(); } void Score::copyCastStxts() { for (Common::HashMap::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) { if (c->_value->_type != kCastText && c->_value->_type != kCastButton) continue; uint stxtid = (_vm->getVersion() < 4) ? c->_key + _castIDoffset : c->_value->_children[0].index; if (_loadedStxts->getVal(stxtid)) { const Stxt *stxt = _loadedStxts->getVal(stxtid); TextCast *tc = (TextCast *)c->_value; tc->importStxt(stxt); } } } void Score::loadSpriteImages(bool isSharedCast) { debugC(1, kDebugLoading, "****** Preloading sprite images"); for (Common::HashMap::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) { if (!c->_value) continue; if (c->_value->_type != kCastBitmap) continue; BitmapCast *bitmapCast = (BitmapCast *)c->_value; uint32 tag = bitmapCast->_tag; uint16 imgId = (uint16)(c->_key + _castIDoffset); if (_vm->getVersion() >= 4 && bitmapCast->_children.size() > 0) { imgId = bitmapCast->_children[0].index; tag = bitmapCast->_children[0].tag; } Image::ImageDecoder *img = NULL; Common::SeekableReadStream *pic = NULL; switch (tag) { case MKTAG('D', 'I', 'B', ' '): if (_movieArchive->hasResource(MKTAG('D', 'I', 'B', ' '), imgId)) { debugC(2, kDebugLoading, "****** Loading 'DIB ' id: %d", imgId); img = new DIBDecoder(); img->loadStream(*_movieArchive->getResource(MKTAG('D', 'I', 'B', ' '), imgId)); bitmapCast->_surface = img->getSurface(); } else if (isSharedCast && _vm->getSharedDIB() != NULL && _vm->getSharedDIB()->contains(imgId)) { debugC(2, kDebugLoading, "****** Loading 'DIB ' id: %d from shared cast", imgId); img = new DIBDecoder(); img->loadStream(*_vm->getSharedDIB()->getVal(imgId)); bitmapCast->_surface = img->getSurface(); } break; case MKTAG('B', 'I', 'T', 'D'): if (isSharedCast) { debugC(2, kDebugLoading, "****** Loading 'BITD' id: %d from shared cast", imgId); pic = _vm->getSharedBMP()->getVal(imgId); if (pic != NULL) pic->seek(0); // TODO: this actually gets re-read every loop... we need to rewind it! } else if (_movieArchive->hasResource(MKTAG('B', 'I', 'T', 'D'), imgId)) { debugC(2, kDebugLoading, "****** Loading 'BITD' id: %d", imgId); pic = _movieArchive->getResource(MKTAG('B', 'I', 'T', 'D'), imgId); } break; default: warning("Unknown Bitmap Cast Tag: [%d] %s", tag, tag2str(tag)); break; } int w = bitmapCast->_initialRect.width(), h = bitmapCast->_initialRect.height(); debugC(4, kDebugImages, "Score::loadSpriteImages(): id: %d, w: %d, h: %d, flags: %x, bytes: %x, bpp: %d clut: %x", imgId, w, h, bitmapCast->_flags, bitmapCast->_bytes, bitmapCast->_bitsPerPixel, bitmapCast->_clut); if (pic != NULL && bitmapCast != NULL && w > 0 && h > 0) { if (_vm->getVersion() < 6) { img = new BITDDecoder(w, h, bitmapCast->_bitsPerPixel, bitmapCast->_pitch); } else { img = new Image::BitmapDecoder(); } img->loadStream(*pic); bitmapCast->_surface = img->getSurface(); } else { warning("Image %d not found", imgId); } } } Score::~Score() { if (_surface && _surface->w) _surface->free(); if (_trailSurface && _trailSurface->w) _trailSurface->free(); if (_backSurface && _backSurface->w) _backSurface->free(); delete _backSurface; delete _surface; delete _trailSurface; if (_movieArchive) _movieArchive->close(); delete _font; delete _labels; delete _loadedStxts; delete _loadedCast; } void Score::loadPalette(Common::SeekableSubReadStreamEndian &stream) { uint16 steps = stream.size() / 6; uint16 index = (steps * 3) - 1; uint16 _paletteColorCount = steps; byte *_palette = new byte[index + 1]; for (uint8 i = 0; i < steps; i++) { _palette[index - 2] = stream.readByte(); stream.readByte(); _palette[index - 1] = stream.readByte(); stream.readByte(); _palette[index] = stream.readByte(); stream.readByte(); index -= 3; } _vm->setPalette(_palette, _paletteColorCount); } void Score::loadFrames(Common::SeekableSubReadStreamEndian &stream) { debugC(1, kDebugLoading, "****** Loading frames VWSC"); //stream.hexdump(stream.size()); uint32 size = stream.readUint32(); size -= 4; if (_vm->getVersion() == 4) { uint32 frame1Offset = stream.readUint32(); uint32 numFrames = stream.readUint32(); uint16 version = stream.readUint16(); uint16 spriteRecordSize = stream.readUint16(); uint16 numChannels = stream.readUint16(); uint16 unk1 = stream.readUint16(); size -= 16; warning("STUB: Score::loadFrames. frame1Offset: %x numFrames: %x version: %x spriteRecordSize: %x numChannels: %x unk1: %x", frame1Offset, numFrames, version, spriteRecordSize, numChannels, unk1); // Unknown, some bytes - constant (refer to contuinity). } else if (_vm->getVersion() > 4) { //what data is up the top of D5 VWSC? uint32 unk1 = stream.readUint32(); uint32 unk2 = stream.readUint32(); uint16 unk3, unk4, unk5, unk6; if (unk2 > 0) { uint32 blockSize = stream.readUint32() - 1; stream.readUint32(); stream.readUint32(); stream.readUint32(); stream.readUint32(); for (uint32 skip = 0; skip < blockSize * 4; skip++) stream.readByte(); //header number two... this is our actual score entry point. unk1 = stream.readUint32(); unk2 = stream.readUint32(); stream.readUint32(); unk3 = stream.readUint16(); unk4 = stream.readUint16(); unk5 = stream.readUint16(); unk6 = stream.readUint16(); } else { unk3 = stream.readUint16(); unk4 = stream.readUint16(); unk5 = stream.readUint16(); unk6 = stream.readUint16(); size -= 16; } warning("STUB: Score::loadFrames. unk1: %x unk2: %x unk3: %x unk4: %x unk5: %x unk6: %x", unk1, unk2, unk3, unk4, unk5, unk6); } uint16 channelSize; uint16 channelOffset; Frame *initial = new Frame(_vm); // Push a frame at frame#0 position. // This makes all indexing simpler _frames.push_back(initial); // This is a representation of the channelData. It gets overridden // partically by channels, hence we keep it and read the score from left to right // // TODO Merge it with shared cast byte channelData[kChannelDataSize]; memset(channelData, 0, kChannelDataSize); while (size != 0 && !stream.eos()) { uint16 frameSize = stream.readUint16(); debugC(kDebugLoading, 8, "++++++++++ score frame %d (frameSize %d) size %d", _frames.size(), frameSize, size); if (frameSize > 0) { Frame *frame = new Frame(_vm); size -= frameSize; frameSize -= 2; while (frameSize != 0) { if (_vm->getVersion() < 4) { channelSize = stream.readByte() * 2; channelOffset = stream.readByte() * 2; frameSize -= channelSize + 2; } else { channelSize = stream.readUint16(); channelOffset = stream.readUint16(); frameSize -= channelSize + 4; } assert(channelOffset + channelSize < kChannelDataSize); stream.read(&channelData[channelOffset], channelSize); } Common::MemoryReadStreamEndian *str = new Common::MemoryReadStreamEndian(channelData, ARRAYSIZE(channelData), stream.isBE()); // str->hexdump(str->size(), 32); frame->readChannels(str); delete str; debugC(8, kDebugLoading, "Score::loadFrames(): Frame %d actionId: %d", _frames.size(), frame->_actionId); _frames.push_back(frame); } else { warning("zero sized frame!? exiting loop until we know what to do with the tags that follow."); size = 0; } } } void Score::loadConfig(Common::SeekableSubReadStreamEndian &stream) { debugC(1, kDebugLoading, "****** Loading Config VWCF"); if (debugChannelSet(5, kDebugLoading)) stream.hexdump(stream.size()); uint16 len = stream.readUint16(); uint16 ver1 = stream.readUint16(); _movieRect = Score::readRect(stream); _castArrayStart = stream.readUint16(); _castArrayEnd = stream.readUint16(); _currentFrameRate = stream.readByte(); if (_currentFrameRate == 0) _currentFrameRate = 20; byte lightswitch = stream.readByte(); uint16 unk1 = stream.readUint16(); uint16 commentFont = stream.readUint16(); uint16 commentSize = stream.readUint16(); uint16 commentStyle = stream.readUint16(); _stageColor = stream.readUint16(); uint16 bitdepth = stream.readUint16(); byte color = stream.readByte(); // boolean, color = 1, B/W = 0 uint16 stageColorR = stream.readUint16(); uint16 stageColorG = stream.readUint16(); uint16 stageColorB = stream.readUint16(); for (int i = 0; i < 0x0b; i++) { stream.readByte(); } if (_vm->getVersion() >= 4) { for (int i = 0; i < 0x16; i++) { stream.readByte(); } int palette = (int16)stream.readUint16(); _vm->setPalette(palette - 1); for (int i = 0; i < 0x08; i++) { stream.readByte(); } } debugC(1, kDebugLoading, "Score::loadConfig(): len: %d, ver: %d, framerate: %d, light: %d, unk: %d, font: %d, size: %d" ", style: %d", len, ver1, _currentFrameRate, lightswitch, unk1, commentFont, commentSize, commentStyle); debugC(1, kDebugLoading, "Score::loadConfig(): stagecolor: %d, depth: %d, color: %d, rgb: 0x%04x 0x%04x 0x%04x", _stageColor, bitdepth, color, stageColorR, stageColorG, stageColorB); if (debugChannelSet(1, kDebugLoading)) _movieRect.debugPrint(1, "Score::loadConfig(): Movie rect: "); } void Score::readVersion(uint32 rid) { _versionMinor = rid & 0xffff; _versionMajor = rid >> 16; debug("Version: %d.%d", _versionMajor, _versionMinor); } void Score::loadCastDataVWCR(Common::SeekableSubReadStreamEndian &stream) { debugC(1, kDebugLoading, "****** Loading Cast rects VWCR. start: %d, end: %d", _castArrayStart, _castArrayEnd); _loadedCast = new Common::HashMap(); for (uint16 id = _castArrayStart; id <= _castArrayEnd; id++) { byte size = stream.readByte(); if (size == 0) continue; if (debugChannelSet(5, kDebugLoading)) stream.hexdump(size); uint8 castType = stream.readByte(); switch (castType) { case kCastBitmap: debugC(3, kDebugLoading, "Score::loadCastDataVWCR(): CastTypes id: %d(%s) BitmapCast", id, numToCastNum(id)); // TODO: Work out the proper tag! _loadedCast->setVal(id, new BitmapCast(stream, MKTAG('B', 'I', 'T', 'D'), _vm->getVersion())); break; case kCastText: debugC(3, kDebugLoading, "Score::loadCastDataVWCR(): CastTypes id: %d(%s) TextCast", id, numToCastNum(id)); _loadedCast->setVal(id, new TextCast(stream, _vm->getVersion())); break; case kCastShape: debugC(3, kDebugLoading, "Score::loadCastDataVWCR(): CastTypes id: %d(%s) ShapeCast", id, numToCastNum(id)); _loadedCast->setVal(id, new ShapeCast(stream, _vm->getVersion())); break; case kCastButton: debugC(3, kDebugLoading, "Score::loadCastDataVWCR(): CastTypes id: %d(%s) ButtonCast", id, numToCastNum(id)); _loadedCast->setVal(id, new ButtonCast(stream, _vm->getVersion())); break; default: warning("Score::loadCastDataVWCR(): Unhandled cast id: %d(%s), type: %d, %d bytes", id, numToCastNum(id), castType, size); stream.skip(size - 1); break; } } } void Score::setSpriteCasts() { // Set cast pointers to sprites for (uint16 i = 0; i < _frames.size(); i++) { for (uint16 j = 0; j < _frames[i]->_sprites.size(); j++) { uint16 castId = _frames[i]->_sprites[j]->_castId; if (castId == 0) continue; if (_vm->getSharedScore() && _vm->getSharedScore()->_loadedCast && _vm->getSharedScore()->_loadedCast->contains(castId)) { _frames[i]->_sprites[j]->_cast = _vm->getSharedScore()->_loadedCast->getVal(castId); } else if (_loadedCast->contains(castId)) { _frames[i]->_sprites[j]->_cast = _loadedCast->getVal(castId); } } } } void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream, uint16 id, Resource *res) { // D4+ variant if (stream.size() == 0) return; // TODO: Determine if there really is a minimum size. // This value was too small for Shape Casts. if (stream.size() < 10) { warning("Score::loadCastData(): CASt data id %d is too small", id); return; } debugC(3, kDebugLoading, "Score::loadCastData(): CASt: id: %d", id); if (debugChannelSet(5, kDebugLoading) && stream.size() < 2048) stream.hexdump(stream.size()); uint32 size1, size2, size3, castType, sizeToRead; byte unk1 = 0, unk2 = 0, unk3 = 0; if (_vm->getVersion() <= 3) { size1 = stream.readUint16(); sizeToRead = size1 +16; // 16 is for bounding rects size2 = stream.readUint32(); size3 = 0; castType = stream.readByte(); unk1 = stream.readByte(); unk2 = stream.readByte(); unk3 = stream.readByte(); } else if (_vm->getVersion() == 4) { size1 = stream.readUint16(); sizeToRead = size1 + 2 + 16; // 16 is for bounding rects size2 = stream.readUint32(); size3 = 0; castType = stream.readByte(); unk1 = stream.readByte(); } else if (_vm->getVersion() == 5) { castType = stream.readUint32(); size3 = stream.readUint32(); size2 = stream.readUint32(); size1 = stream.readUint32(); if (castType == 1) { if (size3 == 0) return; for (uint32 skip = 0; skip < (size1 - 4) / 4; skip++) stream.readUint32(); } sizeToRead = stream.size(); } else { error("Score::loadCastData: unsupported Director version (%d)", _vm->getVersion()); } debugC(3, kDebugLoading, "Score::loadCastData(): CASt: id: %d type: %x size1: %d size2: %d (%x) size3: %d unk1: %d unk2: %d unk3: %d", id, castType, size1, size2, size2, size3, unk1, unk2, unk3); byte *data = (byte *)calloc(sizeToRead, 1); stream.read(data, sizeToRead); Common::MemoryReadStreamEndian castStream(data, sizeToRead, stream.isBE()); switch (castType) { case kCastBitmap: _loadedCast->setVal(id, new BitmapCast(castStream, res->tag, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastBitmap (%d)", res->children.size()); break; case kCastText: _loadedCast->setVal(id, new TextCast(castStream, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastText (%d)", res->children.size()); break; case kCastShape: _loadedCast->setVal(id, new ShapeCast(castStream, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastShape (%d)", res->children.size()); break; case kCastButton: _loadedCast->setVal(id, new ButtonCast(castStream, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastButton (%d)", res->children.size()); break; case kCastLingoScript: _loadedCast->setVal(id, new ScriptCast(castStream, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastLingoScript"); break; case kCastRTE: _loadedCast->setVal(id, new RTECast(castStream, _vm->getVersion())); debugC(3, kDebugLoading, "Score::loadCastData(): loaded kCastRTE (%d)", res->children.size()); break; case kCastFilmLoop: warning("STUB: Score::loadCastData(): kCastFilmLoop (%d)", res->children.size()); size2 = 0; break; case kCastPalette: warning("STUB: Score::loadCastData(): kCastPalette (%d)", res->children.size()); size2 = 0; break; case kCastPicture: warning("STUB: Score::loadCastData(): kCastPicture (%d)", res->children.size()); size2 = 0; break; case kCastSound: warning("STUB: Score::loadCastData(): kCastSound (%d)", res->children.size()); size2 = 0; break; case kCastMovie: warning("STUB: Score::loadCastData(): kCastMovie (%d)", res->children.size()); size2 = 0; break; case kCastDigitalVideo: warning("STUB: Score::loadCastData(): kCastDigitalVideo (%d)", res->children.size()); size2 = 0; break; default: warning("Score::loadCastData(): Unhandled cast type: %d [%s]", castType, tag2str(castType)); // also don't try and read the strings... we don't know what this item is. size2 = 0; break; } if (_loadedCast->contains(id)) // Skip unhandled casts for (uint child = 0; child < res->children.size(); child++) _loadedCast->getVal(id)->_children.push_back(res->children[child]); free(data); if (size2 && _vm->getVersion() < 5) { uint32 entryType = 0; Common::Array castStrings = loadStrings(stream, entryType, false); debugCN(4, kDebugLoading, "Score::loadCastData(): str(%d): '", castStrings.size()); for (uint i = 0; i < castStrings.size(); i++) { debugCN(4, kDebugLoading, "%s'", castStrings[i].c_str()); if (i != castStrings.size() - 1) debugCN(4, kDebugLoading, ", '"); } debugC(4, kDebugLoading, "'"); CastInfo *ci = new CastInfo(); // We have here variable number of strings. Thus, instead of // adding tons of ifs, we use this switch() switch (castStrings.size()) { default: warning("Score::loadCastData(): extra %d strings", castStrings.size() - 5); // fallthrough case 5: ci->type = castStrings[4]; // fallthrough case 4: ci->fileName = castStrings[3]; // fallthrough case 3: ci->directory = castStrings[2]; // fallthrough case 2: ci->name = castStrings[1]; if (!ci->name.empty()) { _castsNames[ci->name] = id; } // fallthrough case 1: ci->script = castStrings[0]; // fallthrough case 0: break; } // FIXME. Disabled by default, requires --debugflags=bytecode for now if (_vm->getVersion() >= 4 && castType == kCastLingoScript && debugChannelSet(-1, kDebugBytecode)) { // Try and load the compiled Lingo script associated with this cast uint scriptId = ((ScriptCast *)(*_loadedCast)[id])->_id - 1; if (scriptId < _castScriptIds.size()) { int resourceId = _castScriptIds[scriptId]; _lingo->addCodeV4(*_movieArchive->getResource(MKTAG('L', 's', 'c', 'r'), resourceId), ((ScriptCast *)_loadedCast->getVal(id))->_scriptType, id); } else { warning("Score::loadCastData(): Lingo context missing a resource entry for script %d referenced in cast %d", scriptId, id); } } else { if (!ci->script.empty()) { // the script type here could be wrong! if (ConfMan.getBool("dump_scripts")) dumpScript(ci->script.c_str(), ((ScriptCast *)_loadedCast->getVal(id))->_scriptType, id); _lingo->addCode(ci->script.c_str(), ((ScriptCast *)_loadedCast->getVal(id))->_scriptType, id); } } _castsInfo[id] = ci; } if (size3) warning("Score::loadCastData(): size3: %x", size3); } void Score::loadCastInto(Sprite *sprite, int castId) { sprite->_cast = _loadedCast->getVal(castId); } Common::Rect Score::getCastMemberInitialRect(int castId) { Cast *cast = _loadedCast->getVal(castId); if (!cast) { warning("Score::getCastMemberInitialRect(%d): empty cast", castId); return Common::Rect(0, 0); } return cast->_initialRect; } void Score::setCastMemberModified(int castId) { Cast *cast = _loadedCast->getVal(castId); if (!cast) { warning("Score::setCastMemberModified(%d): empty cast", castId); return; } cast->_modified = 1; } void Score::loadLabels(Common::SeekableSubReadStreamEndian &stream) { if (debugChannelSet(5, kDebugLoading)) { debug("Score::loadLabels()"); stream.hexdump(stream.size()); } _labels = new Common::SortedArray