aboutsummaryrefslogtreecommitdiff
path: root/engines/director/score.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/director/score.cpp')
-rw-r--r--engines/director/score.cpp849
1 files changed, 849 insertions, 0 deletions
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
new file mode 100644
index 0000000000..8a252a26ed
--- /dev/null
+++ b/engines/director/score.cpp
@@ -0,0 +1,849 @@
+/* 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/events.h"
+
+#include "engines/util.h"
+#include "graphics/font.h"
+#include "graphics/palette.h"
+
+#include "director/score.h"
+#include "director/frame.h"
+#include "director/resource.h"
+#include "director/sound.h"
+#include "director/sprite.h"
+#include "director/lingo/lingo.h"
+
+namespace Director {
+
+static byte defaultPalette[768] = {
+ 0, 0, 0, 17, 17, 17, 34, 34, 34, 68, 68, 68, 85, 85, 85, 119,
+ 119, 119, 136, 136, 136, 170, 170, 170, 187, 187, 187, 221, 221, 221, 238, 238,
+ 238, 0, 0, 17, 0, 0, 34, 0, 0, 68, 0, 0, 85, 0, 0, 119,
+ 0, 0, 136, 0, 0, 170, 0, 0, 187, 0, 0, 221, 0, 0, 238, 0,
+ 17, 0, 0, 34, 0, 0, 68, 0, 0, 85, 0, 0, 119, 0, 0, 136,
+ 0, 0, 170, 0, 0, 187, 0, 0, 221, 0, 0, 238, 0, 17, 0, 0,
+ 34, 0, 0, 68, 0, 0, 85, 0, 0, 119, 0, 0, 136, 0, 0, 170,
+ 0, 0, 187, 0, 0, 221, 0, 0, 238, 0, 0, 0, 0, 51, 0, 0,
+ 102, 0, 0, 153, 0, 0, 204, 0, 0, 255, 0, 51, 0, 0, 51, 51,
+ 0, 51, 102, 0, 51, 153, 0, 51, 204, 0, 51, 255, 0, 102, 0, 0,
+ 102, 51, 0, 102, 102, 0, 102, 153, 0, 102, 204, 0, 102, 255, 0, 153,
+ 0, 0, 153, 51, 0, 153, 102, 0, 153, 153, 0, 153, 204, 0, 153, 255,
+ 0, 204, 0, 0, 204, 51, 0, 204, 102, 0, 204, 153, 0, 204, 204, 0,
+ 204, 255, 0, 255, 0, 0, 255, 51, 0, 255, 102, 0, 255, 153, 0, 255,
+ 204, 0, 255, 255, 51, 0, 0, 51, 0, 51, 51, 0, 102, 51, 0, 153,
+ 51, 0, 204, 51, 0, 255, 51, 51, 0, 51, 51, 51, 51, 51, 102, 51,
+ 51, 153, 51, 51, 204, 51, 51, 255, 51, 102, 0, 51, 102, 51, 51, 102,
+ 102, 51, 102, 153, 51, 102, 204, 51, 102, 255, 51, 153, 0, 51, 153, 51,
+ 51, 153, 102, 51, 153, 153, 51, 153, 204, 51, 153, 255, 51, 204, 0, 51,
+ 204, 51, 51, 204, 102, 51, 204, 153, 51, 204, 204, 51, 204, 255, 51, 255,
+ 0, 51, 255, 51, 51, 255, 102, 51, 255, 153, 51, 255, 204, 51, 255, 255,
+ 102, 0, 0, 102, 0, 51, 102, 0, 102, 102, 0, 153, 102, 0, 204, 102,
+ 0, 255, 102, 51, 0, 102, 51, 51, 102, 51, 102, 102, 51, 153, 102, 51,
+ 204, 102, 51, 255, 102, 102, 0, 102, 102, 51, 102, 102, 102, 102, 102, 153,
+ 102, 102, 204, 102, 102, 255, 102, 153, 0, 102, 153, 51, 102, 153, 102, 102,
+ 153, 153, 102, 153, 204, 102, 153, 255, 102, 204, 0, 102, 204, 51, 102, 204,
+ 102, 102, 204, 153, 102, 204, 204, 102, 204, 255, 102, 255, 0, 102, 255, 51,
+ 102, 255, 102, 102, 255, 153, 102, 255, 204, 102, 255, 255, 153, 0, 0, 153,
+ 0, 51, 153, 0, 102, 153, 0, 153, 153, 0, 204, 153, 0, 255, 153, 51,
+ 0, 153, 51, 51, 153, 51, 102, 153, 51, 153, 153, 51, 204, 153, 51, 255,
+ 153, 102, 0, 153, 102, 51, 153, 102, 102, 153, 102, 153, 153, 102, 204, 153,
+ 102, 255, 153, 153, 0, 153, 153, 51, 153, 153, 102, 153, 153, 153, 153, 153,
+ 204, 153, 153, 255, 153, 204, 0, 153, 204, 51, 153, 204, 102, 153, 204, 153,
+ 153, 204, 204, 153, 204, 255, 153, 255, 0, 153, 255, 51, 153, 255, 102, 153,
+ 255, 153, 153, 255, 204, 153, 255, 255, 204, 0, 0, 204, 0, 51, 204, 0,
+ 102, 204, 0, 153, 204, 0, 204, 204, 0, 255, 204, 51, 0, 204, 51, 51,
+ 204, 51, 102, 204, 51, 153, 204, 51, 204, 204, 51, 255, 204, 102, 0, 204,
+ 102, 51, 204, 102, 102, 204, 102, 153, 204, 102, 204, 204, 102, 255, 204, 153,
+ 0, 204, 153, 51, 204, 153, 102, 204, 153, 153, 204, 153, 204, 204, 153, 255,
+ 204, 204, 0, 204, 204, 51, 204, 204, 102, 204, 204, 153, 204, 204, 204, 204,
+ 204, 255, 204, 255, 0, 204, 255, 51, 204, 255, 102, 204, 255, 153, 204, 255,
+ 204, 204, 255, 255, 255, 0, 0, 255, 0, 51, 255, 0, 102, 255, 0, 153,
+ 255, 0, 204, 255, 0, 255, 255, 51, 0, 255, 51, 51, 255, 51, 102, 255,
+ 51, 153, 255, 51, 204, 255, 51, 255, 255, 102, 0, 255, 102, 51, 255, 102,
+ 102, 255, 102, 153, 255, 102, 204, 255, 102, 255, 255, 153, 0, 255, 153, 51,
+ 255, 153, 102, 255, 153, 153, 255, 153, 204, 255, 153, 255, 255, 204, 0, 255,
+ 204, 51, 255, 204, 102, 255, 204, 153, 255, 204, 204, 255, 204, 255, 255, 255,
+ 0, 255, 255, 51, 255, 255, 102, 255, 255, 153, 255, 255, 204, 255, 255, 255 };
+
+Score::Score(DirectorEngine *vm) {
+ _vm = vm;
+ _surface = new Graphics::ManagedSurface;
+ _trailSurface = new Graphics::ManagedSurface;
+ _movieArchive = _vm->getMainArchive();
+ _lingo = _vm->getLingo();
+ _soundManager = _vm->getSoundManager();
+ _lingo->processEvent(kEventPrepareMovie, 0);
+ _movieScriptCount = 0;
+ _labels = NULL;
+ _font = NULL;
+
+ _versionMinor = _versionMajor = 0;
+ _currentFrameRate = 20;
+ _castArrayStart = _castArrayEnd = 0;
+ _currentFrame = 0;
+ _nextFrameTime = 0;
+ _flags = 0;
+ _stopPlay = false;
+ _stageColor = 0;
+
+ if (_movieArchive->hasResource(MKTAG('M','C','N','M'), 0)) {
+ _macName = _movieArchive->getName(MKTAG('M','C','N','M'), 0).c_str();
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','L','B'), 1024)) {
+ loadLabels(*_movieArchive->getResource(MKTAG('V','W','L','B'), 1024));
+ }
+}
+
+void Score::loadArchive() {
+ Common::Array<uint16> 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");
+ g_system->getPaletteManager()->setPalette(defaultPalette, 0, 256);
+ _vm->setPalette(defaultPalette, 256);
+ } else {
+ Common::SeekableSubReadStreamEndian *pal = _movieArchive->getResource(MKTAG('C', 'L', 'U', 'T'), clutList[0]);
+
+ loadPalette(*pal);
+ g_system->getPaletteManager()->setPalette(_vm->getPalette(), 0, _vm->getPaletteColorCount());
+ }
+
+ assert(_movieArchive->hasResource(MKTAG('V','W','S','C'), 1024));
+ assert(_movieArchive->hasResource(MKTAG('V','W','C','F'), 1024));
+
+ loadFrames(*_movieArchive->getResource(MKTAG('V','W','S','C'), 1024));
+ loadConfig(*_movieArchive->getResource(MKTAG('V','W','C','F'), 1024));
+
+ if (_vm->getVersion() < 4) {
+ assert(_movieArchive->hasResource(MKTAG('V','W','C','R'), 1024));
+ loadCastData(*_movieArchive->getResource(MKTAG('V','W','C','R'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','A','C'), 1024)) {
+ loadActions(*_movieArchive->getResource(MKTAG('V','W','A','C'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','F','I'), 1024)) {
+ loadFileInfo(*_movieArchive->getResource(MKTAG('V','W','F','I'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','F','M'), 1024)) {
+ loadFontMap(*_movieArchive->getResource(MKTAG('V','W','F','M'), 1024));
+ }
+
+ Common::Array<uint16> vwci = _movieArchive->getResourceIDList(MKTAG('V','W','C','I'));
+
+ if (vwci.size() > 0) {
+ Common::Array<uint16>::iterator iterator;
+
+ for (iterator = vwci.begin(); iterator != vwci.end(); ++iterator)
+ loadCastInfo(*_movieArchive->getResource(MKTAG('V','W','C','I'), *iterator), *iterator);
+ }
+
+ Common::Array<uint16> stxt = _movieArchive->getResourceIDList(MKTAG('S','T','X','T'));
+
+ if (stxt.size() > 0) {
+ loadScriptText(*_movieArchive->getResource(MKTAG('S','T','X','T'), *stxt.begin()));
+ }
+}
+
+Score::~Score() {
+ if (_surface)
+ _surface->free();
+
+ if (_trailSurface)
+ _trailSurface->free();
+
+ delete _surface;
+ delete _trailSurface;
+
+ if (_movieArchive)
+ _movieArchive->close();
+
+ delete _font;
+ delete _labels;
+}
+
+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) {
+ uint32 size = stream.readUint32();
+ size -= 4;
+
+ if (_vm->getVersion() > 3) {
+ stream.skip(16);
+ //Unknown, some bytes - constant (refer to contuinity).
+ }
+
+ uint16 channelSize;
+ uint16 channelOffset;
+
+ Frame *initial = new Frame(_vm);
+ _frames.push_back(initial);
+
+ while (size != 0) {
+ uint16 frameSize = stream.readUint16();
+ size -= frameSize;
+ frameSize -= 2;
+ Frame *frame = new Frame(*_frames.back());
+
+ while (frameSize != 0) {
+ if (_vm->getVersion() < 4) {
+ channelSize = stream.readByte() * 2;
+ channelOffset = stream.readByte() * 2;
+ frameSize -= channelSize + 2;
+ } else {
+ channelSize = stream.readByte();
+ channelOffset = stream.readByte();
+ frameSize -= channelSize + 4;
+ }
+ frame->readChannel(stream, channelOffset, channelSize);
+
+ }
+
+ _frames.push_back(frame);
+ }
+
+ //remove initial frame
+ _frames.remove_at(0);
+}
+
+void Score::loadConfig(Common::SeekableSubReadStreamEndian &stream) {
+ /*uint16 unk1 = */ stream.readUint16();
+ /*ver1 = */ stream.readUint16();
+ _movieRect = Score::readRect(stream);
+
+ _castArrayStart = stream.readUint16();
+ _castArrayEnd = stream.readUint16();
+ _currentFrameRate = stream.readByte();
+ stream.skip(9);
+ _stageColor = stream.readUint16();
+}
+
+void Score::readVersion(uint32 rid) {
+ _versionMinor = rid & 0xffff;
+ _versionMajor = rid >> 16;
+
+ debug("Version: %d.%d", _versionMajor, _versionMinor);
+}
+
+void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream) {
+ for (uint16 id = _castArrayStart; id <= _castArrayEnd; id++) {
+ byte size = stream.readByte();
+ if (size == 0)
+ continue;
+
+ uint8 castType = stream.readByte();
+
+ switch (castType) {
+ case kCastBitmap:
+ _casts[id] = new BitmapCast(stream);
+ _casts[id]->type = kCastBitmap;
+ break;
+ case kCastText:
+ _casts[id] = new TextCast(stream);
+ _casts[id]->type = kCastText;
+ break;
+ case kCastShape:
+ _casts[id] = new ShapeCast(stream);
+ _casts[id]->type = kCastShape;
+ break;
+ case kCastButton:
+ _casts[id] = new ButtonCast(stream);
+ _casts[id]->type = kCastButton;
+ break;
+ default:
+ warning("Unhandled cast type: %d", castType);
+ stream.skip(size - 1);
+ break;
+ }
+ }
+
+ //Set cast pointers to sprites
+ for (uint16 i = 0; i < _frames.size(); i++) {
+ for (uint16 j = 0; j < _frames[i]->_sprites.size(); j++) {
+ byte castId = _frames[i]->_sprites[j]->_castId;
+
+ if (_casts.contains(castId))
+ _frames[i]->_sprites[j]->_cast = _casts.find(castId)->_value;
+ }
+ }
+}
+
+void Score::loadLabels(Common::SeekableSubReadStreamEndian &stream) {
+ _labels = new Common::SortedArray<Label *>(compareLabels);
+ uint16 count = stream.readUint16() + 1;
+ uint16 offset = count * 4 + 2;
+
+ uint16 frame = stream.readUint16();
+ uint16 stringPos = stream.readUint16() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 nextFrame = stream.readUint16();
+ uint16 nextStringPos = stream.readUint16() + offset;
+ uint16 streamPos = stream.pos();
+
+ stream.seek(stringPos);
+ Common::String label;
+ for (uint16 j = stringPos; j < nextStringPos; j++) {
+ label += stream.readByte();
+ }
+ _labels->insert(new Label(label, frame));
+ stream.seek(streamPos);
+
+ frame = nextFrame;
+ stringPos = nextStringPos;
+ }
+
+ Common::SortedArray<Label *>::iterator j;
+
+ for (j = _labels->begin(); j != _labels->end(); ++j) {
+ debug("Frame %d, Label %s", (*j)->number, (*j)->name.c_str());
+ }
+}
+
+int Score::compareLabels(const void *a, const void *b) {
+ return ((const Label *)a)->number - ((const Label *)b)->number;
+}
+
+void Score::loadActions(Common::SeekableSubReadStreamEndian &stream) {
+ uint16 count = stream.readUint16() + 1;
+ uint16 offset = count * 4 + 2;
+
+ byte id = stream.readByte();
+ /*byte subId = */ stream.readByte(); //I couldn't find how it used in continuity (except print). Frame actionId = 1 byte.
+ uint16 stringPos = stream.readUint16() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 nextId = stream.readByte();
+ /*byte subId = */ stream.readByte();
+ uint16 nextStringPos = stream.readUint16() + offset;
+ uint16 streamPos = stream.pos();
+
+ stream.seek(stringPos);
+
+ for (uint16 j = stringPos; j < nextStringPos; j++) {
+ byte ch = stream.readByte();
+ if (ch == 0x0d) {
+ ch = '\n';
+ }
+ _actions[id] += ch;
+ }
+
+ stream.seek(streamPos);
+
+ id = nextId;
+ stringPos = nextStringPos;
+
+ if (stringPos == stream.size())
+ break;
+ }
+
+ Common::HashMap<uint16, Common::String>::iterator j;
+
+ if (ConfMan.getBool("dump_scripts"))
+ for (j = _actions.begin(); j != _actions.end(); ++j) {
+ if (!j->_value.empty())
+ dumpScript(j->_value.c_str(), kFrameScript, j->_key);
+ }
+
+ for (j = _actions.begin(); j != _actions.end(); ++j)
+ if (!j->_value.empty())
+ _lingo->addCode(j->_value.c_str(), kFrameScript, j->_key);
+}
+
+void Score::loadScriptText(Common::SeekableSubReadStreamEndian &stream) {
+ /*uint32 unk1 = */ stream.readUint32();
+ uint32 strLen = stream.readUint32();
+ /*uin32 dataLen = */ stream.readUint32();
+ Common::String script;
+
+ for (uint32 i = 0; i < strLen; i++) {
+ byte ch = stream.readByte();
+
+ if (ch == 0x0d) {
+ //in old Mac systems \r was the code for end-of-line instead.
+ ch = '\n';
+ }
+ script += ch;
+ }
+
+ if (!script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(script.c_str(), kMovieScript, _movieScriptCount);
+
+ if (!script.empty())
+ _lingo->addCode(script.c_str(), kMovieScript, _movieScriptCount);
+
+ _movieScriptCount++;
+}
+
+void Score::setStartToLabel(Common::String label) {
+ if (!_labels) {
+ warning("setStartToLabel: No labels set");
+ return;
+ }
+
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == label) {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ warning("Label %s not found", label.c_str());
+}
+
+void Score::dumpScript(const char *script, ScriptType type, uint16 id) {
+ Common::DumpFile out;
+ Common::String typeName;
+ char buf[256];
+
+ switch (type) {
+ case kNoneScript:
+ error("Incorrect dumpScript() call");
+ case kFrameScript:
+ typeName = "frame";
+ break;
+ case kMovieScript:
+ typeName = "movie";
+ break;
+ case kSpriteScript:
+ typeName = "sprite";
+ break;
+ }
+
+ sprintf(buf, "./dumps/%s-%s-%d.txt", _macName.c_str(), typeName.c_str(), id);
+
+ if (!out.open(buf)) {
+ warning("Can not open dump file %s", buf);
+ return;
+ }
+
+ out.write(script, strlen(script));
+
+ out.flush();
+ out.close();
+}
+
+void Score::loadCastInfo(Common::SeekableSubReadStreamEndian &stream, uint16 id) {
+ uint32 entryType = 0;
+ Common::Array<Common::String> castStrings = loadStrings(stream, entryType);
+ CastInfo *ci = new CastInfo();
+
+ ci->script = castStrings[0];
+
+ if (!ci->script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(ci->script.c_str(), kSpriteScript, id);
+
+ if (!ci->script.empty())
+ _lingo->addCode(ci->script.c_str(), kSpriteScript, id);
+
+ ci->name = getString(castStrings[1]);
+ ci->directory = getString(castStrings[2]);
+ ci->fileName = getString(castStrings[3]);
+ ci->type = castStrings[4];
+
+ _castsInfo[id] = ci;
+}
+
+void Score::gotoloop() {
+ //This command has the playback head contonuously return to the first marker to to the left and then loop back.
+ //If no marker are to the left of the playback head, the playback head continues to the right.
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == _currentLabel) {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+}
+
+void Score::gotonext() {
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == _currentLabel) {
+ if (i != _labels->end()) {
+ //return to the first marker to to the right
+ ++i;
+ _currentFrame = (*i)->number;
+ return;
+ } else {
+ //if no markers are to the right of the playback head,
+ //the playback head goes to the first marker to the left
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ }
+ //If there are not markers to the left,
+ //the playback head goes to frame 1, (Director frame array start from 1, engine from 0)
+ _currentFrame = 0;
+}
+
+void Score::gotoprevious() {
+ //One label
+ if (_labels->begin() == _labels->end()) {
+ _currentFrame = (*_labels->begin())->number;
+ return;
+ }
+
+ Common::SortedArray<Label *>::iterator previous = _labels->begin();
+ Common::SortedArray<Label *>::iterator i = previous++;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i, ++previous) {
+ if ((*i)->name == _currentLabel) {
+ _currentFrame = (*previous)->number;
+ return;
+ } else {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ _currentFrame = 0;
+}
+
+Common::String Score::getString(Common::String str) {
+ if (str.size() == 0) {
+ return str;
+ }
+
+ uint8 f = static_cast<uint8>(str.firstChar());
+
+ if (f == 0) {
+ return "";
+ }
+
+ str.deleteChar(0);
+
+ if (str.lastChar() == '\x00') {
+ str.deleteLastChar();
+ }
+
+ return str;
+}
+
+void Score::loadFileInfo(Common::SeekableSubReadStreamEndian &stream) {
+ Common::Array<Common::String> fileInfoStrings = loadStrings(stream, _flags);
+ _script = fileInfoStrings[0];
+
+ if (!_script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(_script.c_str(), kMovieScript, _movieScriptCount);
+
+ if (!_script.empty())
+ _lingo->addCode(_script.c_str(), kMovieScript, _movieScriptCount);
+
+ _movieScriptCount++;
+ _changedBy = fileInfoStrings[1];
+ _createdBy = fileInfoStrings[2];
+ _directory = fileInfoStrings[3];
+}
+
+Common::Array<Common::String> Score::loadStrings(Common::SeekableSubReadStreamEndian &stream, uint32 &entryType, bool hasHeader) {
+ Common::Array<Common::String> strings;
+ uint32 offset = 0;
+
+ if (hasHeader) {
+ offset = stream.readUint32();
+ /*uint32 unk1 = */ stream.readUint32();
+ /*uint32 unk2 = */ stream.readUint32();
+ entryType = stream.readUint32();
+ stream.seek(offset);
+ }
+
+ uint16 count = stream.readUint16();
+ offset += (count + 1) * 4 + 2; //positions info + uint16 count
+ uint32 startPos = stream.readUint32() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ Common::String entryString;
+ uint32 nextPos = stream.readUint32() + offset;
+ uint32 streamPos = stream.pos();
+
+ stream.seek(startPos);
+
+ while (startPos != nextPos) {
+ entryString += stream.readByte();
+ ++startPos;
+ }
+
+ strings.push_back(entryString);
+
+ stream.seek(streamPos);
+ startPos = nextPos;
+ }
+
+ return strings;
+}
+
+void Score::loadFontMap(Common::SeekableSubReadStreamEndian &stream) {
+ uint16 count = stream.readUint16();
+ uint32 offset = (count * 2) + 2;
+ uint16 currentRawPosition = offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 id = stream.readUint16();
+ uint32 positionInfo = stream.pos();
+
+ stream.seek(currentRawPosition);
+
+ uint16 size = stream.readByte();
+ Common::String font;
+
+ for (uint16 k = 0; k < size; k++) {
+ font += stream.readByte();
+ }
+
+ _fontMap[id] = font;
+ debug(3, "Fontmap. ID %d Font %s", id, font.c_str());
+ currentRawPosition = stream.pos();
+ stream.seek(positionInfo);
+ }
+}
+
+BitmapCast::BitmapCast(Common::SeekableSubReadStreamEndian &stream) {
+ flags = stream.readByte();
+ someFlaggyThing = stream.readUint16();
+ initialRect = Score::readRect(stream);
+ boundingRect = Score::readRect(stream);
+ regY = stream.readUint16();
+ regX = stream.readUint16();
+ unk1 = unk2 = 0;
+
+ if (someFlaggyThing & 0x8000) {
+ unk1 = stream.readUint16();
+ unk2 = stream.readUint16();
+ }
+ modified = 0;
+}
+
+TextCast::TextCast(Common::SeekableSubReadStreamEndian &stream) {
+ /*byte flags =*/ stream.readByte();
+ borderSize = static_cast<SizeType>(stream.readByte());
+ gutterSize = static_cast<SizeType>(stream.readByte());
+ boxShadow = static_cast<SizeType>(stream.readByte());
+ textType = static_cast<TextType>(stream.readByte());
+ textAlign = static_cast<TextAlignType>(stream.readUint16());
+ stream.skip(6); //palinfo
+
+ int t = stream.readUint32();
+ assert(t == 0); // So far we saw only 0 here
+
+ initialRect = Score::readRect(stream);
+ textShadow = static_cast<SizeType>(stream.readByte());
+ byte flags = stream.readByte();
+ if (flags & 0x1)
+ textFlags.push_back(kTextFlagEditable);
+ if (flags & 0x2)
+ textFlags.push_back(kTextFlagAutoTab);
+ if (flags & 0x4)
+ textFlags.push_back(kTextFlagDoNotWrap);
+
+ //TODO: FIXME: guesswork
+ fontId = stream.readByte();
+ fontSize = stream.readByte();
+
+ modified = 0;
+}
+
+ShapeCast::ShapeCast(Common::SeekableSubReadStreamEndian &stream) {
+ /*byte flags = */ stream.readByte();
+ /*unk1 = */ stream.readByte();
+ shapeType = static_cast<ShapeType>(stream.readByte());
+ initialRect = Score::readRect(stream);
+ pattern = stream.readUint16BE();
+ fgCol = stream.readByte();
+ bgCol = stream.readByte();
+ fillType = stream.readByte();
+ lineThickness = stream.readByte();
+ lineDirection = stream.readByte();
+ modified = 0;
+}
+
+Common::Rect Score::readRect(Common::SeekableSubReadStreamEndian &stream) {
+ Common::Rect *rect = new Common::Rect();
+ rect->top = stream.readUint16();
+ rect->left = stream.readUint16();
+ rect->bottom = stream.readUint16();
+ rect->right = stream.readUint16();
+
+ return *rect;
+}
+
+void Score::startLoop() {
+ initGraphics(_movieRect.width(), _movieRect.height(), true);
+
+ _surface->create(_movieRect.width(), _movieRect.height());
+ _trailSurface->create(_movieRect.width(), _movieRect.height());
+
+ if (_stageColor == 0)
+ _trailSurface->clear(_vm->getPaletteColorCount() - 1);
+ else
+ _trailSurface->clear(_stageColor);
+
+ _currentFrame = 0;
+ _stopPlay = false;
+ _nextFrameTime = 0;
+
+ _lingo->processEvent(kEventStartMovie, 0);
+ _frames[_currentFrame]->prepareFrame(this);
+
+ while (!_stopPlay && _currentFrame < _frames.size() - 2) {
+ debugC(1, kDebugImages, "Current frame: %d", _currentFrame);
+ update();
+ processEvents();
+ }
+}
+
+void Score::update() {
+ if (g_system->getMillis() < _nextFrameTime)
+ return;
+
+ _surface->clear();
+ _surface->copyFrom(*_trailSurface);
+
+ //Enter and exit from previous frame (Director 4)
+ _lingo->processEvent(kEventEnterFrame, _frames[_currentFrame]->_actionId);
+ _lingo->processEvent(kEventExitFrame, _frames[_currentFrame]->_actionId);
+ //TODO Director 6 - another order
+
+
+ //TODO Director 6 step: send beginSprite event to any sprites whose span begin in the upcoming frame
+ //for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ // if (_frames[_currentFrame]->_sprites[i]->_enabled)
+ // _lingo->processEvent(kEventBeginSprite, i);
+ //}
+
+ //TODO Director 6 step: send prepareFrame event to all sprites and the script channel in upcoming frame
+ //_lingo->processEvent(kEventPrepareFrame, _currentFrame);
+ _currentFrame++;
+
+ Common::SortedArray<Label *>::iterator i;
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->number == _currentFrame) {
+ _currentLabel = (*i)->name;
+ }
+ }
+
+ _frames[_currentFrame]->prepareFrame(this);
+ //Stage is drawn between the prepareFrame and enterFrame events (Lingo in a Nutshell)
+
+ byte tempo = _frames[_currentFrame]->_tempo;
+
+ if (tempo) {
+ if (tempo > 161) {
+ //Delay
+ _nextFrameTime = g_system->getMillis() + (256 - tempo) * 1000;
+
+ return;
+ } else if (tempo <= 60) {
+ //FPS
+ _nextFrameTime = g_system->getMillis() + (float)tempo / 60 * 1000;
+ _currentFrameRate = tempo;
+ } else if (tempo >= 136) {
+ //TODO Wait for channel tempo - 135
+ } else if (tempo == 128) {
+ //TODO Wait for Click/Key
+ } else if (tempo == 135) {
+ //Wait for sound channel 1
+ while (_soundManager->isChannelActive(1)) {
+ processEvents();
+ }
+ } else if (tempo == 134) {
+ //Wait for sound channel 2
+ while (_soundManager->isChannelActive(2)) {
+ processEvents();
+ }
+ }
+ }
+ _nextFrameTime = g_system->getMillis() + (float)_currentFrameRate / 60 * 1000;
+}
+
+void Score::processEvents() {
+ if (_currentFrame > 0)
+ _lingo->processEvent(kEventIdle, _currentFrame - 1);
+
+ Common::Event event;
+
+ int endTime = g_system->getMillis() + 200;
+
+ while (g_system->getMillis() < endTime) {
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_QUIT)
+ _stopPlay = true;
+
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
+ Common::Point pos = g_system->getEventManager()->getMousePos();
+
+ //TODO there is dont send frame id
+ _lingo->processEvent(kEventMouseDown, _frames[_currentFrame]->getSpriteIDFromPos(pos));
+ }
+
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ Common::Point pos = g_system->getEventManager()->getMousePos();
+
+ _lingo->processEvent(kEventMouseUp, _frames[_currentFrame]->getSpriteIDFromPos(pos));
+ }
+ }
+
+ g_system->updateScreen();
+ g_system->delayMillis(10);
+ }
+}
+
+Sprite *Score::getSpriteById(uint16 id) {
+ if (_frames[_currentFrame]->_sprites[id]) {
+ return _frames[_currentFrame]->_sprites[id];
+ } else {
+ warning("Sprite on frame %d width id %d not found", _currentFrame, id);
+ return nullptr;
+ }
+}
+
+} //End of namespace Director