aboutsummaryrefslogtreecommitdiff
path: root/scumm/smush/smush_player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scumm/smush/smush_player.cpp')
-rw-r--r--scumm/smush/smush_player.cpp897
1 files changed, 897 insertions, 0 deletions
diff --git a/scumm/smush/smush_player.cpp b/scumm/smush/smush_player.cpp
new file mode 100644
index 0000000000..d114718d7e
--- /dev/null
+++ b/scumm/smush/smush_player.cpp
@@ -0,0 +1,897 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2003 The ScummVM project
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#include <stdafx.h>
+#include "common/file.h"
+#include "common/util.h"
+#include "common/engine.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse.h"
+#include "sound/mixer.h"
+#include "smush_player.h"
+#include "smush_mixer.h"
+#include "smush_font.h"
+#include "channel.h"
+#include "chunk.h"
+#include "chunk_type.h"
+
+const int MAX_STRINGS = 200;
+
+class StringResource {
+private:
+
+ struct {
+ int id;
+ char *string;
+ } _strings[MAX_STRINGS];
+
+ int _nbStrings;
+ int _lastId;
+ char *_lastString;
+
+public:
+
+ StringResource() :
+ _nbStrings(0),
+ _lastId(-1) {
+ };
+ ~StringResource() {
+ for(int32 i = 0; i < _nbStrings; i++) {
+ delete []_strings[i].string;
+ }
+ }
+
+ bool init(char *buffer, int32 length) {
+ char *def_start = strchr(buffer, '#');
+ while(def_start != NULL) {
+ char *def_end = strchr(def_start, '\n');
+ assert(def_end != NULL);
+
+ char *id_end = def_end;
+ while(id_end >= def_start && !isdigit(*(id_end-1))) {
+ id_end--;
+ }
+
+ assert(id_end > def_start);
+ char *id_start = id_end;
+ while(isdigit(*(id_start - 1))) {
+ id_start--;
+ }
+
+ char idstring[32];
+ memcpy(idstring, id_start, id_end - id_start);
+ idstring[id_end - id_start] = 0;
+ int32 id = atoi(idstring);
+ char *data_start = def_end;
+
+ while(*data_start == '\n' || *data_start == '\r') {
+ data_start++;
+ }
+ char *data_end = data_start;
+
+ while(1) {
+ if(data_end[-2] == '\r' && data_end[1] == '\n' && data_end[-1] == '\n' && data_end[0] == '\r') {
+ break;
+ }
+ data_end++;
+ if(data_end >= buffer + length) {
+ data_end = buffer + length;
+ break;
+ }
+ }
+
+ data_end -= 2;
+ assert(data_end > data_start);
+ char *value = new char[data_end - data_start + 1];
+ assert(value);
+ memcpy(value, data_start, data_end - data_start);
+ value[data_end - data_start] = 0;
+ char *line_start = value;
+ char *line_end;
+
+ while ((line_end = strchr(line_start, '\n'))) {
+ line_start = line_end+1;
+ if (line_start[0] == '/' && line_start[1] == '/') {
+ line_start += 2;
+ if (line_end[-1] == '\r')
+ line_end[-1] = ' ';
+ else
+ *line_end++ = ' ';
+ memmove(line_end, line_start, strlen(line_start)+1);
+ }
+ }
+ _strings[_nbStrings].id = id;
+ _strings[_nbStrings].string = value;
+ _nbStrings ++;
+ def_start = strchr(data_end + 2, '#');
+ }
+ return true;
+ }
+
+ char *get(int id) {
+ if(id == _lastId) {
+ return _lastString;
+ }
+ for(int i = 0; i < _nbStrings; i++) {
+ if(_strings[i].id == id) {
+ _lastId = id;
+ _lastString = _strings[i].string;
+ return _strings[i].string;
+ }
+ }
+ warning("invalid string id : %d", id);
+ _lastId = -1;
+ _lastString = "unknown string";
+ return _lastString;
+ }
+};
+
+static StringResource *getStrings(const char *file, const char *directory, bool is_encoded) {
+ debug(7, "trying to read text ressources from %s", file);
+ File theFile;
+
+ theFile.open(file, directory);
+ if (!theFile.isOpen()) {
+ return 0;
+ }
+ int32 length = theFile.size();
+ char *filebuffer = new char [length + 1];
+ assert(filebuffer);
+ theFile.read(filebuffer, length);
+ filebuffer[length] = 0;
+
+ if(is_encoded) {
+ static const int32 ETRS_HEADER_LENGTH = 16;
+ assert(length > ETRS_HEADER_LENGTH);
+ Chunk::type type = READ_BE_UINT32(filebuffer);
+
+ if(type != TYPE_ETRS) {
+ delete [] filebuffer;
+ return getStrings(file, directory, false);
+ }
+
+ char *old = filebuffer;
+ filebuffer = new char[length - ETRS_HEADER_LENGTH + 1];
+ for(int32 i = ETRS_HEADER_LENGTH; i < length; i++) {
+ filebuffer[i - ETRS_HEADER_LENGTH] = old[i] ^ 0xCC;
+ }
+ filebuffer[length - ETRS_HEADER_LENGTH] = '\0';
+ delete []old;
+ length -= ETRS_HEADER_LENGTH;
+ }
+ StringResource *sr = new StringResource;
+ assert(sr);
+ sr->init(filebuffer, length);
+ delete []filebuffer;
+ return sr;
+}
+
+SmushPlayer *player;
+
+void smush_callback(void *ptr) {
+ player->_smushProcessFrame = true;
+ player->parseNextFrame();
+ player->_smushProcessFrame = false;
+}
+
+SmushPlayer::SmushPlayer(Scumm *scumm, int speed, bool subtitles) {
+ player = this;
+ _version = -1;
+ _nbframes = 0;
+ _smixer = 0;
+ _strings = NULL;
+ _skipNext = false;
+ _data = NULL;
+ _storeFrame = false;
+ _width = 0;
+ _height = 0;
+ _frameBuffer = NULL;
+ _sf[0] = NULL;
+ _sf[1] = NULL;
+ _sf[2] = NULL;
+ _sf[3] = NULL;
+ _sf[4] = NULL;
+ _scumm = scumm;
+ _IACTchannel = -1,
+ _IACTpos = 0;
+ _soundFrequency = 22050;
+ _speed = speed;
+ _subtitles = subtitles;
+ _smushProcessFrame = false;
+}
+
+SmushPlayer::~SmushPlayer() {
+ deinit();
+}
+
+void SmushPlayer::init() {
+
+ _frame = 0;
+
+ _scumm->_sound->pauseBundleMusic(true);
+ if (_scumm->_imuseDigital) {
+ _scumm->_imuseDigital->pause(true);
+ }
+ _scumm->_videoFinished = false;
+ _scumm->_insaneState = true;
+
+ _smixer = new SmushMixer(_scumm->_mixer);
+
+ _scumm->setDirtyColors(0, 255);
+ _smixer->_silentMixer = _scumm->_silentDigitalImuse;
+ _scumm->_smushPlay = true;
+ _data = _scumm->virtscr[0].screenPtr + _scumm->virtscr[0].xstart;
+ _scumm->_timer->installProcedure(&smush_callback, _speed);
+
+ _alreadyInit = false;
+}
+
+void SmushPlayer::deinit() {
+ _scumm->_smushPlay = false;
+ _scumm->_timer->releaseProcedure(&smush_callback);
+ while (_smushProcessFrame) {}
+
+ for(int i = 0; i < 5; i++) {
+ if (_sf[i]) {
+ delete _sf[i];
+ _sf[i] = NULL;
+ }
+ }
+
+ if(_strings) {
+ delete _strings;
+ _strings = NULL;
+ }
+
+ if(_smixer) {
+ _smixer->stop();
+ delete _smixer;
+ _smixer = NULL;
+ }
+
+ _scumm->_insaneState = false;
+ _scumm->exitCutscene();
+ if (_scumm->_imuseDigital) {
+ _scumm->_imuseDigital->pause(false);
+ }
+ _scumm->_sound->pauseBundleMusic(false);
+ _scumm->_fullRedraw = 1;
+}
+
+void SmushPlayer::checkBlock(const Chunk &b, Chunk::type type_expected, uint32 min_size) {
+ if(type_expected != b.getType()) {
+ error("Chunk type is different from expected : %d != %d", b.getType(), type_expected);
+ }
+ if(min_size > b.getSize()) {
+ error("Chunk size is inferior than minimum required size : %d < %d", b.getSize(), min_size);
+ }
+}
+
+void SmushPlayer::handleSoundBuffer(int32 track_id, int32 index, int32 max_frames, int32 flags, int32 vol, int32 bal, Chunk &b, int32 size) {
+ debug(6, "SmushPlayer::handleSoundBuffer(%d)", track_id);
+// if((flags & 128) == 128) {
+// return;
+// }
+// if((flags & 64) == 64) {
+// return;
+// }
+ SmushChannel *c = _smixer->findChannel(track_id);
+ if(c == NULL) {
+ c = new SaudChannel(track_id, _soundFrequency);
+ _smixer->addChannel(c);
+ }
+ if(index == 0) {
+ c->setParameters(max_frames, flags, vol, bal);
+ } else {
+ c->checkParameters(index, max_frames, flags, vol, bal);
+ }
+ c->appendData(b, size);
+}
+
+void SmushPlayer::handleSoundFrame(Chunk &b) {
+ checkBlock(b, TYPE_PSAD);
+ debug(6, "SmushPlayer::handleSoundFrame()");
+
+ int32 track_id = b.getWord();
+ int32 index = b.getWord();
+ int32 max_frames = b.getWord();
+ int32 flags = b.getWord();
+ int32 vol = b.getByte();
+ int32 bal = b.getChar();
+#ifdef DEBUG
+ if(index == 0) {
+ debug(5, "track_id == %d, max_frames == %d, %d, %d, %d", track_id, max_frames, flags, vol, bal);
+ }
+#endif
+ int32 size = b.getSize() - 10;
+ handleSoundBuffer(track_id, index, max_frames, flags, vol, bal, b, size);
+}
+
+void SmushPlayer::handleSkip(Chunk &b) {
+ checkBlock(b, TYPE_SKIP, 4);
+ int32 code = b.getDword();
+ debug(6, "SmushPlayer::handleSkip(%d)", code);
+ if(code >= 0 && code < 37)
+ _skipNext = _skips[code];
+ else
+ _skipNext = true;
+}
+
+void SmushPlayer::handleStore(Chunk &b) {
+ debug(6, "SmushPlayer::handleStore()");
+ checkBlock(b, TYPE_STOR, 4);
+ _storeFrame = true;
+}
+
+void SmushPlayer::handleFetch(Chunk &b) {
+ debug(6, "SmushPlayer::handleFetch()");
+ checkBlock(b, TYPE_FTCH, 6);
+
+ if (_frameBuffer != NULL) {
+ memcpy(_data, _frameBuffer, _width * _height);
+ }
+}
+
+void SmushPlayer::handleImuseBuffer(int32 track_id, int32 index, int32 nbframes, int32 size, int32 unk1, int32 track_flags, Chunk &b, int32 bsize) {
+ int32 track = (track_flags << 16) | track_id;
+
+ SmushChannel *c = _smixer->findChannel(track);
+ if(c == 0) {
+ c = new ImuseChannel(track, _soundFrequency);
+ _smixer->addChannel(c);
+ }
+ if(index == 0)
+ c->setParameters(nbframes, size, track_flags, unk1);
+ else
+ c->checkParameters(index, nbframes, size, track_flags, unk1);
+ c->appendData(b, bsize);
+}
+
+void SmushPlayer::handleImuseAction(Chunk &b) {
+ checkBlock(b, TYPE_IACT, 8);
+ debug(6, "SmushPlayer::handleImuseAction()");
+
+ int code = b.getWord();
+ int flags = b.getWord();
+ int unknown = b.getShort();
+ int track_flags = b.getWord();
+
+ assert(flags == 46 && unknown == 0);
+ int track_id = b.getWord();
+ int index = b.getWord();
+ int nbframes = b.getWord();
+ int32 size = b.getDword();
+ int32 bsize = b.getSize() - 18;
+
+ if (g_scumm->_gameId != GID_CMI) {
+ handleImuseBuffer(track_id, index, nbframes, size, unknown, track_flags, b, bsize);
+ } else {
+ byte output_data[4096];
+ byte *src = (byte *)malloc(bsize);
+ b.read(src, bsize);
+ byte *d_src = src;
+ byte value;
+
+ do {
+ if (bsize == 0)
+ break;
+ if (_IACTpos >= 2) {
+ int32 len = READ_BE_UINT16(_IACToutput) + 2;
+ len -= _IACTpos;
+ if (len > bsize) {
+ memcpy(_IACToutput + _IACTpos, d_src, bsize);
+ _IACTpos += bsize;
+ bsize = 0;
+ } else {
+ memcpy(_IACToutput + _IACTpos, d_src, len);
+ byte *dst = output_data;
+ byte *d_src2 = _IACToutput;
+ d_src2 += 2;
+ int32 count = 1024;
+ byte variable1 = *d_src2++;
+ byte variable2 = variable1 >> 4;
+ variable1 &= 0x0f;
+ do {
+ value = *(d_src2++);
+ if (value == 0x80) {
+ *dst++ = *d_src2++;
+ *dst++ = *d_src2++;
+ } else {
+ int16 val = (int8)value << variable2;
+ *dst++ = val>> 8;
+ *dst++ = (byte)(val);
+ }
+ value = *(d_src2++);
+ if (value == 0x80) {
+ *dst++ = *d_src2++;
+ *dst++ = *d_src2++;
+ } else {
+ int16 val = (int8)value << variable1;
+ *dst++ = val>> 8;
+ *dst++ = (byte)(val);
+ }
+ } while (--count);
+
+ if (_IACTchannel == -1) {
+ _IACTchannel = _scumm->_mixer->playStream(NULL, -1, output_data, 0x1000, 22050,
+ SoundMixer::FLAG_STEREO | SoundMixer::FLAG_16BITS, -1, 200000);
+ } else {
+ _scumm->_mixer->append(_IACTchannel, output_data, 0x1000, 22050,
+ SoundMixer::FLAG_STEREO | SoundMixer::FLAG_16BITS);
+ }
+
+ bsize -= len;
+ d_src += len;
+ _IACTpos = 0;
+ }
+ } else {
+ if (bsize == 1) {
+ if (_IACTpos != 0) {
+ *(_IACToutput + 1) = *d_src++;
+ _IACTpos = 2;
+ bsize--;
+ continue;
+ }
+ bsize = 0;
+ *(_IACToutput + 0) = *d_src;
+ _IACTpos = 1;
+ continue;
+ } else if (_IACTpos == 0) {
+ *(_IACToutput + 0) = *d_src++;
+ bsize--;
+ }
+ *(_IACToutput + 1) = *d_src++;
+ _IACTpos = 2;
+ bsize--;
+ }
+ } while (bsize != 0);
+
+ free(src);
+ }
+}
+
+void SmushPlayer::handleTextResource(Chunk &b) {
+ int pos_x = b.getShort();
+ int pos_y = b.getShort();
+ int flags = b.getShort();
+ int left = b.getShort();
+ int top = b.getShort();
+ int width = b.getShort();
+ /*int32 height =*/ b.getShort();
+ /*int32 unk2 =*/ b.getWord();
+
+ char *str, *string = NULL, *string2 = NULL;
+ if (b.getType() == TYPE_TEXT) {
+ string = (char *)malloc(b.getSize() - 16);
+ str = string;
+ b.read(string, b.getSize() - 16);
+ } else {
+ int string_id = b.getWord();
+ if(!_strings)
+ return;
+ str = _strings->get(string_id);
+ }
+
+ // if subtitles disabled and bit 3 is set, then do not draw
+ if((!_subtitles) && ((flags & 8) == 8))
+ return;
+
+ SmushFont *sf = _sf[0];
+ int color = 15;
+ while(*str == '/') {
+ str++; // For Full Throttle text resources
+ }
+
+ if (_scumm->_gameId == GID_CMI) {
+ _scumm->translateText((byte*)str - 1, _scumm->_transText);
+ while(*str++ != '/');
+ string2 = (char *)_scumm->_transText;
+
+ // If string2 contains formatting information there probably
+ // wasn't any translation for it in the language.tab file. In
+ // that case, pretend there is no string2.
+ if (string2[0] == '^')
+ string2[0] = 0;
+ }
+
+ while(str[0] == '^') {
+ switch(str[1]) {
+ case 'f':
+ {
+ int id = str[3] - '0';
+ str += 4;
+ sf = _sf[id];
+ }
+ break;
+ case 'c':
+ {
+ color = str[4] - '0' + 10 *(str[3] - '0');
+ str += 5;
+ }
+ break;
+ default:
+ error("invalid escape code in text string");
+ }
+ }
+
+ assert(sf != NULL);
+ sf->setColor(color);
+
+ if (_scumm->_gameId != GID_CMI) {
+ string2 = str;
+ }
+ if (_scumm->_gameId == GID_CMI) {
+ if (string2[0] == 0) {
+ string2 = str;
+ }
+ }
+
+ // flags:
+ // bit 0 - center 1
+ // bit 1 - not used 2
+ // bit 2 - ??? 4
+ // bit 3 - wrap around 8
+ switch (flags) {
+ case 0:
+ sf->drawStringAbsolute(string2, _data, _width, pos_x, pos_y);
+ break;
+ case 1:
+ sf->drawStringCentered(string2, _data, _width, _height, MAX(pos_y, top), left, width, pos_x);
+ break;
+ case 4:
+ sf->drawStringAbsolute(string2, _data, _width, pos_x, pos_y);
+ break;
+ case 5:
+ sf->drawStringCentered(string2, _data, _width, _height, MAX(pos_y, top), left, width, pos_x);
+ break;
+ case 8:
+ sf->drawStringWrap(string2, _data, _width, _height, pos_x, MAX(pos_y, top), width);
+ break;
+ case 9:
+ sf->drawStringCentered(string2, _data, _width, _height, MAX(pos_y, top), left, width, pos_x);
+ break;
+ case 12:
+ sf->drawStringWrap(string2, _data, _width, _height, pos_x, MAX(pos_y, top), width);
+ break;
+ case 13:
+ sf->drawStringWrapCentered(string2, _data, _width, _height, pos_x, MAX(pos_y, top), width);
+ break;
+ default:
+ warning("SmushPlayer::handleTextResource. Not handled flags: %d\n", flags);
+ }
+
+ if (string != NULL) {
+ free (string);
+ }
+}
+
+bool SmushPlayer::readString(const char *file, const char *directory) {
+ const char *i = strrchr(file, '.');
+ if(i == NULL) {
+ error("invalid filename : %s", file);
+ }
+ char fname[260];
+ memcpy(fname, file, i - file);
+ strcpy(fname + (i - file), ".trs");
+ if((_strings = getStrings(fname, directory, false)) != 0) {
+ return true;
+ }
+
+ if((_strings = getStrings("digtxt.trs", directory, true)) != 0) {
+ return true;
+ }
+ return false;
+}
+
+void SmushPlayer::readPalette(byte *out, Chunk &in) {
+ in.read(out, 0x300);
+}
+
+static byte delta_color(byte org_color, int16 delta_color) {
+ int16 t;
+ t = (((int32)(org_color) << 7) + org_color + delta_color) >> 7;
+ if (t > 255)
+ t = 255;
+ if (t < 0)
+ t = 0;
+ return (byte)t;
+}
+
+void SmushPlayer::handleDeltaPalette(Chunk &b) {
+ checkBlock(b, TYPE_XPAL);
+ debug(6, "SmushPlayer::handleDeltaPalette()");
+
+ if(b.getSize() == 0x300 * 3 + 4) {
+
+ b.getWord();
+ b.getWord();
+
+ for(int i = 0; i < 0x300; i++) {
+ _deltaPal[i] = b.getWord();
+ }
+ readPalette(_pal, b);
+ setPalette(_pal);
+ } else if(b.getSize() == 6) {
+
+ b.getWord();
+ b.getWord();
+ b.getWord();
+
+ for(int i = 0; i < 0x300; i++) {
+ _pal[i] = delta_color(_pal[i], _deltaPal[i]);
+ }
+ setPalette(_pal);
+ } else {
+ error("SmushPlayer::handleDeltaPalette() Wrong size for DeltaPalette");
+ }
+}
+
+void SmushPlayer::handleNewPalette(Chunk &b) {
+ checkBlock(b, TYPE_NPAL, 0x300);
+ debug(6, "SmushPlayer::handleNewPalette()");
+
+ readPalette(_pal, b);
+ setPalette(_pal);
+}
+
+void SmushPlayer::initCodecs() {
+}
+
+extern void smush_decode_codec1(byte *dst, byte *src, int height);
+
+void SmushPlayer::handleFrameObject(Chunk &b) {
+ checkBlock(b, TYPE_FOBJ, 14);
+ if(_skipNext) {
+ _skipNext = false;
+ return;
+ }
+
+ int codec = b.getWord();
+ b.getWord(); // left
+ b.getWord(); // top
+ int width = b.getWord();
+ int height = b.getWord();
+
+ // hack for spontanic changing resolution
+ // differ than width scumm screen, and pass only one width 384x242
+ if((height != _scumm->_realHeight) && (height != 242))
+ return;
+ if (height == 242)
+ return;
+ if((width != _scumm->_realWidth) && (width != 384))
+ return;
+ if (width == 384)
+ return;
+
+ if(_alreadyInit == false) {
+ _codec37.init(width, height);
+ _codec47.init(width, height);
+ _alreadyInit = true;
+ }
+
+ _width = width;
+ _height = height;
+ b.getWord();
+ b.getWord();
+
+
+ int32 chunk_size = b.getSize() - 14;
+ byte *chunk_buffer = (byte *)malloc(chunk_size);
+ assert(chunk_buffer);
+ b.read(chunk_buffer, chunk_size);
+
+ switch (codec) {
+ case 1:
+ case 3:
+ smush_decode_codec1(_data, chunk_buffer, _height);
+ break;
+ case 37:
+ _codec37.decode(_data, chunk_buffer);
+ break;
+ case 47:
+ _codec47.decode(_data, chunk_buffer);
+ break;
+ default:
+ error("Invalid codec for frame object : %d", (int)codec);
+ }
+
+ if (_storeFrame == true) {
+ if (_frameBuffer == NULL) {
+ _frameBuffer = (byte *)malloc(_width * _height);
+ }
+ memcpy(_frameBuffer, _data, _width * _height);
+ _storeFrame = false;
+ }
+
+ free(chunk_buffer);
+}
+
+void SmushPlayer::handleFrame(Chunk &b) {
+ checkBlock(b, TYPE_FRME);
+ debug(6, "SmushPlayer::handleFrame(%d)", _frame);
+ _skipNext = false;
+
+ uint32 start_time, curr_time, start_update, end_update;
+ start_time = _scumm->_system->get_msecs();
+
+ while(!b.eof()) {
+ Chunk *sub = b.subBlock();
+ if(sub->getSize() & 1) b.seek(1);
+ switch(sub->getType()) {
+ case TYPE_NPAL:
+ handleNewPalette(*sub);
+ break;
+ case TYPE_FOBJ:
+ handleFrameObject(*sub);
+ break;
+ case TYPE_PSAD:
+ handleSoundFrame(*sub);
+ break;
+ case TYPE_TRES:
+ handleTextResource(*sub);
+ break;
+ case TYPE_XPAL:
+ handleDeltaPalette(*sub);
+ break;
+ case TYPE_IACT:
+ handleImuseAction(*sub);
+ break;
+ case TYPE_STOR:
+ handleStore(*sub);
+ break;
+ case TYPE_FTCH:
+ handleFetch(*sub);
+ break;
+ case TYPE_SKIP:
+ handleSkip(*sub);
+ break;
+ case TYPE_TEXT:
+ handleTextResource(*sub);
+ break;
+ default:
+ error("Unknown frame subChunk found : %s, %d", Chunk::ChunkString(sub->getType()), sub->getSize());
+ }
+ delete sub;
+ }
+
+ curr_time = _scumm->_system->get_msecs();
+
+ _smixer->handleFrame();
+
+ start_update = _scumm->_system->get_msecs();
+ if (curr_time < (start_time + _speed / 1000)) {
+ updateScreen();
+ } else {
+ warning("SmushPlayer: skipping update frame %d", _frame);
+ }
+ end_update = _scumm->_system->get_msecs();
+ debug(0, "Smush stats: FRME( %03d ),GFX_update( %03d ),FRME+GFX+SFX( %03d ),Limit(%d)",
+ curr_time - start_time, end_update - start_update,
+ end_update - start_time, _speed / 1000);
+
+ _frame++;
+}
+
+void SmushPlayer::handleAnimHeader(Chunk &b) {
+ checkBlock(b, TYPE_AHDR, 0x300 + 6);
+ debug(6, "SmushPlayer::handleAnimHeader()");
+
+ _version = b.getWord();
+ _nbframes = b.getWord();
+ b.getWord();
+ readPalette(_pal, b);
+ setPalette(_pal);
+}
+
+void SmushPlayer::setupAnim(const char *file, const char *directory) {
+ _base = new FileChunk(file, directory);
+ Chunk *sub = _base->subBlock();
+ checkBlock(*sub, TYPE_AHDR);
+ handleAnimHeader(*sub);
+
+ readString(file, directory);
+
+ if (_scumm->_gameId == GID_FT) {
+ _sf[0] = new SmushFont(true, false);
+ _sf[2] = new SmushFont(true, false);
+ _sf[0]->loadFont("scummfnt.nut", directory);
+ _sf[2]->loadFont("titlfnt.nut", directory);
+ } else if (_scumm->_gameId == GID_DIG) {
+ for(int i = 0; i < 4; i++) {
+ char file_font[11];
+ sprintf((char *)&file_font, "font%d.nut", i);
+ _sf[i] = new SmushFont(i != 0, false);
+ _sf[i]->loadFont(file_font, directory);
+ }
+ } else if (_scumm->_gameId == GID_CMI) {
+ for(int i = 0; i < 5; i++) {
+ char file_font[11];
+ sprintf((char *)&file_font, "font%d.nut", i);
+ _sf[i] = new SmushFont(false, true);
+ _sf[i]->loadFont(file_font, directory);
+ }
+ } else {
+ error("SmushPlayer::init() Unknown font setup for game");
+ }
+
+ delete sub;
+}
+
+void SmushPlayer::parseNextFrame() {
+ if (_scumm->_smushPlay == false)
+ return;
+
+ Chunk *sub = _base->subBlock();
+ if (_base->eof()) {
+ _scumm->_videoFinished = true;
+ return;
+ }
+
+ switch(sub->getType()) {
+ case TYPE_FRME:
+ handleFrame(*sub);
+ break;
+ default:
+ error("Unknown Chunk found : %d, %d", sub->getType(), sub->getSize());
+ }
+ delete sub;
+}
+
+void SmushPlayer::setPalette(byte *palette) {
+ byte palette_colors[1024];
+ byte *p = palette_colors;
+ byte *data = palette;
+
+ for (int i = 0; i != 256; i++, data += 3, p += 4) {
+ p[0] = data[0]; // red
+ p[1] = data[1]; // green
+ p[2] = data[2]; // blue
+ p[3] = 0;
+ }
+
+ _scumm->_system->set_palette(palette_colors, 0, 256);
+}
+
+void SmushPlayer::updateScreen() {
+ int width = MIN(_width, _scumm->_realWidth);
+ int height = MIN(_height, _scumm->_realHeight);
+
+ _scumm->parseEvents();
+ _scumm->_system->copy_rect(_data, _width, 0, 0, width, height);
+ _scumm->_system->update_screen();
+}
+
+void SmushPlayer::play(const char *filename, const char *directory) {
+ setupAnim(filename, directory);
+ init();
+
+ while (true) {
+ _scumm->processKbd();
+ _scumm->_system->delay_msecs(10);
+ if (_scumm->_videoFinished == true)
+ break;
+ if (_scumm->_saveLoadFlag)
+ break;
+ };
+
+ deinit();
+}