diff options
Diffstat (limited to 'engines/composer')
-rw-r--r-- | engines/composer/composer.cpp | 169 | ||||
-rw-r--r-- | engines/composer/composer.h | 39 | ||||
-rw-r--r-- | engines/composer/detection.cpp | 24 | ||||
-rw-r--r-- | engines/composer/graphics.cpp | 73 | ||||
-rw-r--r-- | engines/composer/resource.cpp | 68 | ||||
-rw-r--r-- | engines/composer/resource.h | 22 | ||||
-rw-r--r-- | engines/composer/scripting.cpp | 260 |
7 files changed, 601 insertions, 54 deletions
diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 085ce815dd..556dad7e94 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -32,6 +32,7 @@ #include "graphics/cursorman.h" #include "graphics/surface.h" #include "graphics/pixelformat.h" +#include "graphics/wincursor.h" #include "engines/util.h" #include "engines/advancedDetector.h" @@ -52,6 +53,11 @@ ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gam ComposerEngine::~ComposerEngine() { DebugMan.clearAllDebugChannels(); + stopPipes(); + for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) + delete *i; + for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) + delete *i; for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) delete i->_archive; for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) @@ -99,6 +105,11 @@ Common::Error ComposerEngine::run() { _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); _needsUpdate = true; + Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor(); + CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), + cursor->getHotspotY(), cursor->getKeyColor()); + CursorMan.replaceCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount()); + loadLibrary(0); _currentTime = 0; @@ -150,6 +161,7 @@ Common::Error ComposerEngine::run() { redraw(); + tickOldScripts(); processAnimFrame(); } else if (_needsUpdate) { redraw(); @@ -213,11 +225,13 @@ void ComposerEngine::onMouseDown(const Common::Point &pos) { if (!button) return; + debug(3, "mouseDown on button id %d", button->_id); + // TODO: other buttons? uint16 buttonsDown = 1; // MK_LBUTTON uint16 spriteId = sprite ? sprite->_id : 0; - runScript(button->_scriptId, button->_id, buttonsDown, spriteId); + runScript(button->_scriptId, (getGameType() == GType_ComposerV1) ? 0 : button->_id, buttonsDown, spriteId); } void ComposerEngine::onMouseMove(const Common::Point &pos) { @@ -233,21 +247,48 @@ void ComposerEngine::onMouseMove(const Common::Point &pos) { const Button *button = getButtonFor(sprite, pos); if (_lastButton != button) { if (_lastButton && _lastButton->_scriptIdRollOff) - runScript(_lastButton->_scriptIdRollOff, _lastButton->_id, buttonsDown, 0); + runScript(_lastButton->_scriptIdRollOff, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0); _lastButton = button; if (_lastButton && _lastButton->_scriptIdRollOn) - runScript(_lastButton->_scriptIdRollOn, _lastButton->_id, buttonsDown, 0); + runScript(_lastButton->_scriptIdRollOn, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0); } if (_mouseSpriteId) { addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset); - _needsUpdate = true; } + _needsUpdate = true; } void ComposerEngine::onKeyDown(uint16 keyCode) { runEvent(kEventKeyDown, keyCode, 0, 0); runEvent(kEventChar, keyCode, 0, 0); + + for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) { + for (Common::List<KeyboardHandler>::iterator j = i->_keyboardHandlers.begin(); j != i->_keyboardHandlers.end(); j++) { + const KeyboardHandler &handler = *j; + if (keyCode != handler.keyId) + continue; + + int modifiers = g_system->getEventManager()->getModifierState(); + switch (handler.modifierId) { + case 0x10: // shift + if (!(modifiers & Common::KBD_SHIFT)) + continue; + break; + case 0x11: // control + if (!(modifiers & Common::KBD_CTRL)) + continue; + break; + case 0: + break; + default: + warning("unknown keyb modifier %d", handler.modifierId); + continue; + } + + runScript(handler.scriptId); + } + } } void ComposerEngine::setCursor(uint16 id, const Common::Point &offset) { @@ -269,11 +310,15 @@ void ComposerEngine::setCursorVisible(bool visible) { _mouseVisible = true; if (_mouseSpriteId) addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset); + else + CursorMan.showMouse(true); onMouseMove(_lastMousePos); } else if (!visible && _mouseVisible) { _mouseVisible = false; if (_mouseSpriteId) removeSprite(_mouseSpriteId, 0); + else + CursorMan.showMouse(false); } } @@ -316,9 +361,42 @@ Common::String ComposerEngine::mangleFilename(Common::String filename) { } void ComposerEngine::loadLibrary(uint id) { - if (!id) - id = atoi(getStringFromConfig("Common", "StartUp").c_str()); - Common::String filename = getFilename("Libs", 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.clear(); + for (uint i = 0; i < filename.size(); i++) { + if (filename[i] == '\\' || filename[i] == ':') + continue; + for (uint j = 0; j < filename.size(); j++) { + if (filename[j] == '.') + break; + _bookGroup += filename[j]; + } + break; + } + } else { + if (!id) + id = atoi(getStringFromConfig("Common", "StartUp").c_str()); + filename = getFilename("Libs", id); + } Library library; @@ -348,6 +426,37 @@ void ComposerEngine::loadLibrary(uint id) { 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); @@ -373,10 +482,9 @@ void ComposerEngine::unloadLibrary(uint id) { delete *j; } _anims.clear(); - for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) { - delete *j; - } - _pipes.clear(); + stopPipes(); + + _randomEvents.clear(); for (Common::List<Sprite>::iterator j = _sprites.begin(); j != _sprites.end(); j++) { j->_surface.free(); @@ -426,13 +534,14 @@ Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) { _type = stream->readUint16LE(); _active = (_type & 0x8000) ? true : false; + bool hasRollover = (gameType == GType_ComposerV1) && (_type & 0x4000); _type &= 0xfff; debug(9, "button %d: type %d, active %d", id, _type, _active); - uint16 flags = 0; uint16 size = 4; if (gameType == GType_ComposerV1) { - flags = stream->readUint16LE(); + stream->skip(2); + _zorder = 0; _scriptId = stream->readUint16LE(); _scriptIdRollOn = 0; @@ -469,7 +578,7 @@ Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) { error("unknown button type %d", _type); } - if (flags & 0x40) { + if (hasRollover) { _scriptIdRollOn = stream->readUint16LE(); _scriptIdRollOff = stream->readUint16LE(); } @@ -477,6 +586,26 @@ Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) { delete stream; } +// AMBI-style button +Button::Button(Common::SeekableReadStream *stream) { + _id = 0; + _zorder = 0; + _active = true; + _type = kButtonSprites; + _scriptIdRollOn = 0; + _scriptIdRollOff = 0; + + _scriptId = stream->readUint16LE(); + + uint16 count = stream->readUint16LE(); + for (uint j = 0; j < count; j++) { + uint16 spriteId = stream->readUint16LE(); + _spriteIds.push_back(spriteId); + } + + delete stream; +} + bool Button::contains(const Common::Point &pos) const { switch (_type) { case kButtonRect: @@ -524,4 +653,16 @@ const Button *ComposerEngine::getButtonFor(const Sprite *sprite, const Common::P return NULL; } +void ComposerEngine::setButtonActive(uint16 id, bool active) { + for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) { + for (Common::List<Button>::iterator i = l->_buttons.begin(); i != l->_buttons.end(); i++) { + if (i->_id != id) + continue; + i->_active = active; + } + } + + onMouseMove(_lastMousePos); +} + } // End of namespace Composer diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 03e895b59e..0f53258289 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -69,6 +69,7 @@ class Button { public: Button() { } Button(Common::SeekableReadStream *stream, uint16 id, uint gameType); + Button(Common::SeekableReadStream *stream); bool contains(const Common::Point &pos) const; @@ -94,11 +95,23 @@ enum { kEventKeyUp = 7 }; +struct KeyboardHandler { + uint16 keyId; + uint16 modifierId; + uint16 scriptId; +}; + +struct RandomEvent { + uint16 weight; + uint16 scriptId; +}; + struct Library { uint _id; Archive *_archive; Common::List<Button> _buttons; + Common::List<KeyboardHandler> _keyboardHandlers; }; struct QueuedScript { @@ -116,6 +129,19 @@ struct PendingPageChange { bool _remove; }; +struct OldScript { + OldScript(uint16 id, Common::SeekableReadStream *stream); + ~OldScript(); + + uint16 _id; + + uint32 _size; + Common::SeekableReadStream *_stream; + + uint16 _zorder; + uint32 _currDelay; +}; + class ComposerEngine : public Engine { protected: Common::Error run(); @@ -149,15 +175,20 @@ private: uint _directoriesToStrip; Common::ConfigFile _bookIni; + Common::String _bookGroup; Common::List<Library> _libraries; Common::Array<PendingPageChange> _pendingPageChanges; Common::Array<uint16> _stack; Common::Array<uint16> _vars; + Common::List<OldScript *> _oldScripts; Common::Array<QueuedScript> _queuedScripts; Common::List<Animation *> _anims; Common::List<Pipe *> _pipes; + Common::Array<Common::SeekableReadStream *> _pipeStreams; + + Common::HashMap<uint16, Common::Array<RandomEvent> > _randomEvents; void onMouseDown(const Common::Point &pos); void onMouseMove(const Common::Point &pos); @@ -188,17 +219,25 @@ private: void setArg(uint16 arg, uint16 type, uint16 val); void runScript(uint16 id); int16 scriptFuncCall(uint16 id, int16 param1, int16 param2, int16 param3); + void runOldScript(uint16 id, uint16 wait); + void stopOldScript(uint16 id); + void tickOldScripts(); + bool tickOldScript(OldScript *script); void playAnimation(uint16 animId, int16 param1, int16 param2, int16 param3); void stopAnimation(Animation *anim, bool localOnly = false, bool pipesOnly = false); void playWaveForAnim(uint16 id, uint16 priority, bool bufferingOnly); void processAnimFrame(); + void playPipe(uint16 id); + void stopPipes(); + bool spriteVisible(uint16 id, uint16 animId); Sprite *addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos); void removeSprite(uint16 id, uint16 animId); const Sprite *getSpriteAtPos(const Common::Point &pos); const Button *getButtonFor(const Sprite *sprite, const Common::Point &pos); + void setButtonActive(uint16 id, bool active); void dirtySprite(const Sprite &sprite); void redraw(); diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp index 2ca00f9f53..af355402c2 100644 --- a/engines/composer/detection.cpp +++ b/engines/composer/detection.cpp @@ -63,8 +63,6 @@ static const PlainGameDescriptor composerGames[] = { namespace Composer { -using Common::GUIO_NONE; - static const ComposerGameDescription gameDescriptions[] = { // from Liam Finds a Story CD { @@ -75,7 +73,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_USEEXTRAASTITLE | ADGF_DEMO, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV1 }, @@ -88,7 +86,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -102,7 +100,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_USEEXTRAASTITLE | ADGF_DEMO, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -119,7 +117,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -132,7 +130,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -145,7 +143,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::FR_FRA, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -162,7 +160,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -175,7 +173,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -188,7 +186,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -205,7 +203,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, @@ -222,7 +220,7 @@ static const ComposerGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - Common::GUIO_NONE + GUIO1(GUIO_NOASPECT) }, GType_ComposerV2 }, diff --git a/engines/composer/graphics.cpp b/engines/composer/graphics.cpp index f253d85ad7..1314e903ae 100644 --- a/engines/composer/graphics.cpp +++ b/engines/composer/graphics.cpp @@ -123,8 +123,10 @@ void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventP // If the resource is a pipe itself, then load the pipe // and then fish the requested animation out of it. if (type != 1) { + _pipeStreams.push_back(stream); newPipe = new Pipe(stream); _pipes.push_front(newPipe); + newPipe->nextFrame(); stream = newPipe->getResource(ID_ANIM, animId, false); } } @@ -187,8 +189,10 @@ void ComposerEngine::playWaveForAnim(uint16 id, uint16 priority, bool bufferingO } } Common::SeekableReadStream *stream = NULL; + bool fromPipe = true; if (!bufferingOnly && hasResource(ID_WAVE, id)) { stream = getResource(ID_WAVE, id); + fromPipe = false; } else { for (Common::List<Pipe *>::iterator k = _pipes.begin(); k != _pipes.end(); k++) { Pipe *pipe = *k; @@ -200,12 +204,18 @@ void ComposerEngine::playWaveForAnim(uint16 id, uint16 priority, bool bufferingO } if (!stream) return; - // FIXME: non-pipe buffers have fixed wav header (data at +44, size at +40) - byte *buffer = (byte *)malloc(stream->size()); - stream->read(buffer, stream->size()); + + uint32 size = stream->size(); + if (!fromPipe) { + // non-pipe buffers have fixed wav header (data at +44, size at +40) + stream->skip(40); + size = stream->readUint32LE(); + } + byte *buffer = (byte *)malloc(size); + stream->read(buffer, size); if (!_audioStream) _audioStream = Audio::makeQueuingAudioStream(22050, false); - _audioStream->queueBuffer(buffer, stream->size(), DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); _currSoundPriority = priority; delete stream; if (!_mixer->isSoundHandleActive(_soundHandle)) @@ -351,9 +361,50 @@ void ComposerEngine::processAnimFrame() { for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) { Pipe *pipe = *j; pipe->nextFrame(); + + // V1 pipe audio; see OldPipe + if (pipe->hasResource(ID_WAVE, 0xffff)) + playWaveForAnim(0xffff, 0, false); } } +void ComposerEngine::playPipe(uint16 id) { + stopPipes(); + + if (!hasResource(ID_PIPE, id)) { + error("couldn't find pipe %d", id); + } + + Common::SeekableReadStream *stream = getResource(ID_PIPE, id); + OldPipe *pipe = new OldPipe(stream); + _pipes.push_front(pipe); + //pipe->nextFrame(); + + const Common::Array<uint16> *scripts = pipe->getScripts(); + if (scripts && !scripts->empty()) + runScript((*scripts)[0], 1, 0, 0); +} + +void ComposerEngine::stopPipes() { + for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) { + const Common::Array<uint16> *scripts = (*j)->getScripts(); + if (scripts) { + for (uint i = 0; i < scripts->size(); i++) { + removeSprite((*scripts)[i], 0); + stopOldScript((*scripts)[i]); + } + } + delete *j; + } + + _pipes.clear(); + + // substreams may need to remain valid until the end of a page + for (uint i = 0; i < _pipeStreams.size(); i++) + delete _pipeStreams[i]; + _pipeStreams.clear(); +} + bool ComposerEngine::spriteVisible(uint16 id, uint16 animId) { for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) { if (i->_id != id) @@ -375,7 +426,10 @@ Sprite *ComposerEngine::addSprite(uint16 id, uint16 animId, uint16 zorder, const for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) { if (i->_id != id) continue; - if (i->_animId && animId && (i->_animId != animId)) + if (getGameType() == GType_ComposerV1) { + if (i->_animId != animId) + continue; + } else if (i->_animId && animId && (i->_animId != animId)) continue; dirtySprite(*i); @@ -425,7 +479,10 @@ void ComposerEngine::removeSprite(uint16 id, uint16 animId) { for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) { if (!i->_id || (id && i->_id != id)) continue; - if (i->_animId && animId && (i->_animId != animId)) + if (getGameType() == GType_ComposerV1) { + if (i->_animId != animId) + continue; + } else if (i->_animId && animId && (i->_animId != animId)) continue; dirtySprite(*i); i->_surface.free(); @@ -499,8 +556,8 @@ void ComposerEngine::loadCTBL(uint16 id, uint fadePercent) { uint16 numEntries = stream->readUint16LE(); debug(1, "CTBL: %d entries", numEntries); - assert(numEntries <= 256); - assert(stream->size() == 2 + (numEntries * 3)); + if ((numEntries > 256) || (stream->size() < 2 + (numEntries * 3))) + error("CTBL %d was invalid (%d entries, size %d)", id, numEntries, stream->size()); byte buffer[256 * 3]; stream->read(buffer, numEntries * 3); diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp index b40bdb379b..a4e292747c 100644 --- a/engines/composer/resource.cpp +++ b/engines/composer/resource.cpp @@ -252,8 +252,9 @@ Pipe::Pipe(Common::SeekableReadStream *stream) { _offset = 0; _stream = stream; _anim = NULL; +} - nextFrame(); +Pipe::~Pipe() { } void Pipe::nextFrame() { @@ -334,4 +335,69 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); } +OldPipe::OldPipe(Common::SeekableReadStream *stream) : Pipe(stream), _currFrame(0) { + uint32 tag = _stream->readUint32BE(); + if (tag != ID_PIPE) + error("invalid tag for pipe (%08x)", tag); + + _numFrames = _stream->readUint32LE(); + uint16 scriptCount = _stream->readUint16LE(); + _scripts.reserve(scriptCount); + for (uint i = 0; i < scriptCount; i++) + _scripts.push_back(_stream->readUint16LE()); + + _offset = _stream->pos(); +} + +void OldPipe::nextFrame() { + if (_currFrame >= _numFrames) + return; + + _stream->seek(_offset, SEEK_SET); + + uint32 tag = _stream->readUint32BE(); + if (tag != ID_FRME) + error("invalid tag for pipe (%08x)", tag); + + uint16 spriteCount = _stream->readUint16LE(); + uint32 spriteSize = _stream->readUint32LE(); + uint32 audioSize = _stream->readUint32LE(); + + Common::Array<uint16> spriteIds; + Common::Array<PipeResourceEntry> spriteEntries; + for (uint i = 0; i < spriteCount; i++) { + spriteIds.push_back(_stream->readUint16LE()); + PipeResourceEntry entry; + entry.size = _stream->readUint32LE(); + entry.offset = _stream->readUint32LE(); + spriteEntries.push_back(entry); + } + + uint32 spriteDataOffset = _stream->pos(); + _stream->skip(spriteSize); + + ResourceMap &spriteResMap = _types[ID_BMAP]; + spriteResMap.clear(); + for (uint i = 0; i < spriteIds.size(); i++) { + PipeResourceEntry &entry = spriteEntries[i]; + entry.offset += spriteDataOffset; + spriteResMap[spriteIds[i]].entries.push_back(entry); + } + + ResourceMap &audioResMap = _types[ID_WAVE]; + audioResMap.clear(); + + if (audioSize > 0) { + PipeResourceEntry entry; + entry.size = audioSize; + entry.offset = _stream->pos(); + // we use 0xffff for per-frame pipe audio + audioResMap[0xffff].entries.push_back(entry); + _stream->skip(audioSize); + } + + _offset = _stream->pos(); + _currFrame++; +} + } // End of namespace Composer diff --git a/engines/composer/resource.h b/engines/composer/resource.h index 9408cdffb8..e0052cd868 100644 --- a/engines/composer/resource.h +++ b/engines/composer/resource.h @@ -35,16 +35,21 @@ struct Animation; #define ID_LBRC MKTAG('L','B','R','C') // Main FourCC +#define ID_ACEL MKTAG('A','C','E','L') // Keyboard Accelerator (v1) +#define ID_AMBI MKTAG('A','M','B','I') // Ambient (v1 sprite button) #define ID_ANIM MKTAG('A','N','I','M') // Animation #define ID_BMAP MKTAG('B','M','A','P') // Bitmap #define ID_BUTN MKTAG('B','U','T','N') // Button #define ID_CTBL MKTAG('C','T','B','L') // Color Table #define ID_EVNT MKTAG('E','V','N','T') // Event #define ID_PIPE MKTAG('P','I','P','E') // Pipe +#define ID_RAND MKTAG('R','A','N','D') // Random Object #define ID_SCRP MKTAG('S','C','R','P') // Script #define ID_VARI MKTAG('V','A','R','I') // Variables #define ID_WAVE MKTAG('W','A','V','E') // Wave +#define ID_FRME MKTAG('F','R','M','E') // Frame + class Archive { public: Archive(); @@ -102,13 +107,16 @@ struct PipeResource { class Pipe { public: Pipe(Common::SeekableReadStream *stream); - void nextFrame(); + virtual ~Pipe(); + virtual void nextFrame(); Animation *_anim; bool hasResource(uint32 tag, uint16 id) const; Common::SeekableReadStream *getResource(uint32 tag, uint16 id, bool buffering); + virtual const Common::Array<uint16> *getScripts() { return NULL; } + protected: Common::SeekableReadStream *_stream; @@ -119,6 +127,18 @@ protected: uint32 _offset; }; +class OldPipe : public Pipe { +public: + OldPipe(Common::SeekableReadStream *stream); + void nextFrame(); + + const Common::Array<uint16> *getScripts() { return &_scripts; } + +protected: + uint32 _currFrame, _numFrames; + Common::Array<uint16> _scripts; +}; + } // End of namespace Composer #endif diff --git a/engines/composer/scripting.cpp b/engines/composer/scripting.cpp index 1989919233..3a201c841a 100644 --- a/engines/composer/scripting.cpp +++ b/engines/composer/scripting.cpp @@ -122,6 +122,11 @@ void ComposerEngine::runEvent(uint16 id, int16 param1, int16 param2, int16 param } int16 ComposerEngine::runScript(uint16 id, int16 param1, int16 param2, int16 param3) { + if (getGameType() == GType_ComposerV1) { + runOldScript(id, param1); + return 0; + } + _vars[1] = param1; _vars[2] = param2; _vars[3] = param3; @@ -155,10 +160,14 @@ void ComposerEngine::setArg(uint16 arg, uint16 type, uint16 val) { default: error("invalid argument type %d (setting arg %d)", type, arg); } - } void ComposerEngine::runScript(uint16 id) { + if (getGameType() == GType_ComposerV1) { + runOldScript(id, 0); + return; + } + if (!hasResource(ID_SCRP, id)) { debug(1, "ignoring attempt to run script %d, because it doesn't exist", id); return; @@ -561,25 +570,11 @@ int16 ComposerEngine::scriptFuncCall(uint16 id, int16 param1, int16 param2, int1 return 0; case kFuncActivateButton: debug(3, "kFuncActivateButton(%d)", param1); - for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) { - for (Common::List<Button>::iterator i = l->_buttons.begin(); i != l->_buttons.end(); i++) { - if (i->_id != param1) - continue; - i->_active = true; - } - } - onMouseMove(_lastMousePos); + setButtonActive(param1, true); return 1; case kFuncDeactivateButton: debug(3, "kFuncDeactivateButton(%d)", param1); - for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) { - for (Common::List<Button>::iterator i = l->_buttons.begin(); i != l->_buttons.end(); i++) { - if (i->_id != param1) - continue; - i->_active = false; - } - } - onMouseMove(_lastMousePos); + setButtonActive(param1, false); return 1; case kFuncNewPage: debug(3, "kFuncNewPage(%d, %d)", param1, param2); @@ -726,4 +721,235 @@ int16 ComposerEngine::scriptFuncCall(uint16 id, int16 param1, int16 param2, int1 } } +OldScript::OldScript(uint16 id, Common::SeekableReadStream *stream) : _id(id), _stream(stream) { + _size = _stream->readUint32LE(); + _stream->skip(2); + _currDelay = 0; + _zorder = 10; +} + +OldScript::~OldScript() { + delete _stream; +} + +void ComposerEngine::runOldScript(uint16 id, uint16 wait) { + stopOldScript(id); + + Common::SeekableReadStream *stream = getResource(ID_SCRP, id); + OldScript *script = new OldScript(id, stream); + script->_currDelay = wait; + _oldScripts.push_back(script); +} + +void ComposerEngine::stopOldScript(uint16 id) { + // FIXME: this could potentially (in the case of buggy script) be called on an in-use script + + for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) { + if ((*i)->_id == id) { + delete *i; + i = _oldScripts.reverse_erase(i); + } + } +} + +void ComposerEngine::tickOldScripts() { + for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) { + if (!tickOldScript(*i)) { + delete *i; + i = _oldScripts.reverse_erase(i); + } + } +} + +enum { + kOldOpNoOp = 0, + kOldOpReplaceSprite = 1, + kOldOpPlayWav = 2, + kOldOpRunScript = 3, + kOldOpStopScript = 4, + kOldOpActivateButton = 5, + kOldOpDeactivateButton = 6, + kOldOpDrawSprite = 7, + kOldOpRemoveSprite = 8, + kOldOpDisableMouseInput = 9, + kOldOpEnableMouseInput = 10, + kOldOpWait = 11, + kOldOpRandWait = 12, + kOldOpDrawGlobalSprite = 13, + kOldOpRemoveGlobalSprite = 14, + kOldOpSetZOrder = 15, + kOldOpPlayPipe = 16, + kOldOpStopPipe = 17, + kOldOpNewScreen = 20, + kOldOpRunRandom = 22 +}; + +bool ComposerEngine::tickOldScript(OldScript *script) { + if (script->_currDelay) { + script->_currDelay--; + return true; + } + + bool running = true; + bool erasedOldSprite = false; + while (running && script->_stream->pos() < (int)script->_size) { + uint16 spriteId, scriptId, buttonId, pipeId; + Common::Point spritePos; + + script->_stream->skip(0); + byte op = script->_stream->readByte(); + switch (op) { + case kOldOpNoOp: + debug(3, "kOldOpNoOp()"); + running = false; + break; + case kOldOpReplaceSprite: + if (!erasedOldSprite) { + removeSprite(0, script->_id); + erasedOldSprite = true; + } + + spriteId = script->_stream->readUint16LE(); + spritePos.x = script->_stream->readSint16LE(); + spritePos.y = script->_stream->readSint16LE(); + debug(3, "kOldOpReplaceSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y); + addSprite(spriteId, script->_id, script->_zorder, spritePos); + break; + case kOldOpPlayWav: + uint16 wavId, prio; + wavId = script->_stream->readUint16LE(); + prio = script->_stream->readUint16LE(); + debug(3, "kOldOpPlayWav(%d, %d)", wavId, prio); + playWaveForAnim(wavId, prio, false); + break; + case kOldOpRunScript: + scriptId = script->_stream->readUint16LE(); + debug(3, "kOldOpRunScript(%d)", scriptId); + if (scriptId == script->_id) { + // reset ourselves + removeSprite(0, script->_id); + script->_stream->seek(6); + } else { + runScript(scriptId); + } + break; + case kOldOpStopScript: + scriptId = script->_stream->readUint16LE(); + debug(3, "kOldOpStopScript(%d)", scriptId); + removeSprite(0, scriptId); + stopOldScript(scriptId); + break; + case kOldOpActivateButton: + buttonId = script->_stream->readUint16LE(); + debug(3, "kOldOpActivateButton(%d)", buttonId); + setButtonActive(buttonId, true); + break; + case kOldOpDeactivateButton: + buttonId = script->_stream->readUint16LE(); + debug(3, "kOldOpDeactivateButton(%d)", buttonId); + setButtonActive(buttonId, false); + break; + case kOldOpDrawSprite: + spriteId = script->_stream->readUint16LE(); + spritePos.x = script->_stream->readSint16LE(); + spritePos.y = script->_stream->readSint16LE(); + debug(3, "kOldOpDrawSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y); + addSprite(spriteId, script->_id, script->_zorder, spritePos); + break; + case kOldOpRemoveSprite: + spriteId = script->_stream->readUint16LE(); + debug(3, "kOldOpRemoveSprite(%d)", spriteId); + removeSprite(spriteId, script->_id); + break; + case kOldOpDisableMouseInput: + debug(3, "kOldOpDisableMouseInput()"); + setCursorVisible(false); + break; + case kOldOpEnableMouseInput: + debug(3, "kOldOpEnableMouseInput()"); + setCursorVisible(true); + break; + case kOldOpWait: + script->_currDelay = script->_stream->readUint16LE(); + debug(3, "kOldOpWait(%d)", script->_currDelay); + break; + case kOldOpRandWait: + uint16 min, max; + min = script->_stream->readUint16LE(); + max = script->_stream->readUint16LE(); + debug(3, "kOldOpRandWait(%d, %d)", min, max); + script->_currDelay = _rnd->getRandomNumberRng(min, max); + break; + case kOldOpDrawGlobalSprite: + spriteId = script->_stream->readUint16LE(); + spritePos.x = script->_stream->readSint16LE(); + spritePos.y = script->_stream->readSint16LE(); + debug(3, "kOldOpDrawGlobalSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y); + addSprite(spriteId, 0, script->_zorder, spritePos); + break; + case kOldOpRemoveGlobalSprite: + spriteId = script->_stream->readUint16LE(); + debug(3, "kOldOpRemoveGlobalSprite(%d)", spriteId); + removeSprite(spriteId, 0); + break; + case kOldOpSetZOrder: + script->_zorder = script->_stream->readUint16LE(); + debug(3, "kOldOpSetZOrder(%d)", script->_zorder); + break; + case kOldOpPlayPipe: + pipeId = script->_stream->readUint16LE(); + debug(3, "kOldOpPlayPipe(%d)", pipeId); + playPipe(pipeId); + break; + case kOldOpStopPipe: + pipeId = script->_stream->readUint16LE(); + debug(3, "kOldOpStopPipe(%d)", pipeId); + // yes, pipeId is ignored here.. + stopPipes(); + break; + case kOldOpNewScreen: + uint16 newScreenId; + newScreenId = script->_stream->readUint16LE(); + debug(3, "kOldOpNewScreen(%d)", newScreenId); + if (!newScreenId) { + quitGame(); + } else { + _pendingPageChanges.clear(); + _pendingPageChanges.push_back(PendingPageChange(newScreenId, false)); + } + break; + case kOldOpRunRandom: + uint16 randomId; + randomId = script->_stream->readUint16LE(); + debug(3, "kOldOpRunRandom(%d)", randomId); + if (!_randomEvents.contains(randomId)) { + warning("kOldOpRunRandom found no entries for id %d", randomId); + } else { + uint32 randValue = _rnd->getRandomNumberRng(0, 32767); + const Common::Array<RandomEvent> &events = _randomEvents[randomId]; + uint i = 0; + for (i = 0; i < events.size(); i++) { + if ((i + 1 == events.size()) || (randValue <= events[i].weight)) { + runScript(events[i].scriptId); + break; + } else { + randValue -= events[i].weight; + } + } + } + break; + default: + error("unknown oldScript op %d", op); + } + } + + if (script->_stream->pos() >= (int)script->_size) { + // stop running if we ran out of script + removeSprite(0, script->_id); + return false; + } + + return true; +} + } // End of namespace Composer |