aboutsummaryrefslogtreecommitdiff
path: root/engines/composer
diff options
context:
space:
mode:
Diffstat (limited to 'engines/composer')
-rw-r--r--engines/composer/composer.cpp169
-rw-r--r--engines/composer/composer.h39
-rw-r--r--engines/composer/detection.cpp24
-rw-r--r--engines/composer/graphics.cpp73
-rw-r--r--engines/composer/resource.cpp68
-rw-r--r--engines/composer/resource.h22
-rw-r--r--engines/composer/scripting.cpp260
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