[Scummvm-git-logs] scummvm master -> 32fb690aae8d7beaa2440065f1029e28c89a137a

bluegr noreply at scummvm.org
Sun Jan 23 22:24:50 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
32fb690aae TRECISION: Add support for audio in Amiga video


Commit: 32fb690aae8d7beaa2440065f1029e28c89a137a
    https://github.com/scummvm/scummvm/commit/32fb690aae8d7beaa2440065f1029e28c89a137a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2022-01-24T00:24:28+02:00

Commit Message:
TRECISION: Add support for audio in Amiga video

The audio is in raw format, like the rest of the sounds in the Amiga
port.

Overall:
- A new class has been created, NightlongVideoDecoder, which
  encapsulates the PC and Amiga video playing functionality
- The AnimManager class has been moved into animmanager.*
- The AnimType class has been moved from anim.* to animtype.*

Changed paths:
  A engines/trecision/animmanager.cpp
  A engines/trecision/animmanager.h
  A engines/trecision/animtype.cpp
  A engines/trecision/animtype.h
  R engines/trecision/anim.cpp
  R engines/trecision/anim.h
    engines/trecision/dialog.cpp
    engines/trecision/graphics.cpp
    engines/trecision/inventory.cpp
    engines/trecision/logic.cpp
    engines/trecision/module.mk
    engines/trecision/pathfinding3d.cpp
    engines/trecision/renderer3d.cpp
    engines/trecision/resource.cpp
    engines/trecision/saveload.cpp
    engines/trecision/script.cpp
    engines/trecision/text.cpp
    engines/trecision/trecision.cpp
    engines/trecision/video.cpp
    engines/trecision/video.h


diff --git a/engines/trecision/animmanager.cpp b/engines/trecision/animmanager.cpp
new file mode 100644
index 00000000000..6007d492b6e
--- /dev/null
+++ b/engines/trecision/animmanager.cpp
@@ -0,0 +1,548 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+
+#include "audio/decoders/raw.h"
+
+#include "trecision/actor.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
+#include "trecision/defines.h"
+#include "trecision/dialog.h"
+#include "trecision/graphics.h"
+#include "trecision/sound.h"
+#include "trecision/text.h"
+#include "trecision/trecision.h"
+#include "trecision/video.h"
+
+namespace Trecision {
+
+AnimManager::AnimManager(TrecisionEngine *vm) : _vm(vm) {
+	for (int i = 0; i < MAXACTIVEANIM; ++i) {
+		_animations[i] = nullptr;
+		_playingAnims[i] = 0;
+	}
+
+	for (int i = 0; i < MAXANIM; ++i) {
+		_animTab[i]._flag = 0;
+		_animTab[i]._name[0] = '\0';
+	}
+
+	_curCD = 1;
+	swapCD(_curCD);
+
+	_bgAnimRestarted = false;
+}
+
+AnimManager::~AnimManager() {
+	for (int i = 0; i < MAXACTIVEANIM; ++i) {
+		delete _animations[i];
+		_animations[i] = nullptr;
+		_animFile[i].close();
+	}
+}
+
+void AnimManager::playMovie(const Common::String &filename, int startFrame, int endFrame, bool singleChoice) {
+	NightlongVideoDecoder *smkDecoder = new NightlongVideoDecoder(_vm->isAmiga());
+
+	if (!smkDecoder->loadFile(filename)) {
+		warning("playMovie: File %s not found", filename.c_str());
+		delete smkDecoder;
+		_vm->_dialogMgr->afterChoice();
+		return;
+	}
+
+	Common::Event event;
+	bool skipVideo = false;
+	uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
+	uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
+	_vm->_drawText._text.clear();
+
+	smkDecoder->start();
+
+	// WORKAROUND: If the video has a single choice, and it starts from
+	// the beginning, ignore the calculated end frame and play all of it
+	if (singleChoice && startFrame < 10 && endFrame < (int)smkDecoder->getFrameCount() - 1)
+		endFrame = smkDecoder->getFrameCount() - 1;
+
+	setVideoRange(smkDecoder, startFrame, endFrame);
+
+	while (!_vm->shouldQuit() && startFrame != endFrame && !smkDecoder->endOfVideo() && !skipVideo) {
+		if (smkDecoder->needsUpdate()) {
+			drawFrame(smkDecoder, x, y, true);
+		}
+
+		while (_vm->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYUP && event.kbd.keycode == Common::KEYCODE_ESCAPE)
+				skipVideo = true;
+		}
+
+		g_system->delayMillis(10);
+	}
+
+	delete smkDecoder;
+
+	_vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
+	_vm->freeKey();
+	_vm->_dialogMgr->afterChoice();
+}
+
+void AnimManager::setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame) {
+	// Trecision starts at 1 but ScummVM starts at 0
+	startFrame = CLIP<int32>(startFrame - 1, 0, smkDecoder->getFrameCount() - 1);
+	endFrame = CLIP<int32>(endFrame - 1, 0, smkDecoder->getFrameCount() - 1);
+
+	// If choices are attached
+	if (startFrame > 0 && startFrame > smkDecoder->getCurFrame()) {
+		smkDecoder->forceSeekToFrame(startFrame - 1);
+	}
+	smkDecoder->setEndFrame(endFrame);
+}
+
+void AnimManager::drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen) {
+	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+	if (frame) {
+		Graphics::Surface *frame16 = frame->convertTo(g_system->getScreenFormat(), smkDecoder->getPalette());
+		drawFrameSubtitles(frame16, smkDecoder->getCurFrame());
+		g_system->copyRectToScreen(frame16->getPixels(), frame16->pitch, x, y, frame16->w, frame16->h);
+		frame16->free();
+		delete frame16;
+
+		if (updateScreen)
+			_vm->_system->updateScreen();
+	}
+}
+
+void AnimManager::drawFrameSubtitles(Graphics::Surface *surface, int frameNum) {
+	if (!ConfMan.getBool("subtitles"))
+		return;
+
+	_vm->_dialogMgr->dialogHandler(frameNum);
+	if (_vm->_drawText._text.empty())
+		return;
+
+	// Subtitles can be placed in different coordinates in the video,
+	// which are set inside dialogHandler(), but are then reset to
+	// fixed coordinates
+	_vm->_drawText._rect.left = 20;
+	_vm->_drawText._rect.top = 380 - TOP;
+	_vm->_drawText._rect.setWidth(MAXX - 40);
+	_vm->_drawText._rect.setHeight(_vm->_drawText.calcHeight(_vm));
+	_vm->_drawText._subtitleRect = Common::Rect(MAXX, MAXY);
+	_vm->_drawText.draw(_vm, false, surface);
+}
+
+void AnimManager::openSmkAnim(int slot, const Common::String &name) {
+	for (int i = 0; i < 3; i++) {
+		// Open the animation, or swap the 3 CDs to find it
+		if (_animFile[slot].hasFile(name)) {
+			openSmk(slot, _animFile[slot].createReadStreamForMember(name));
+			return;
+		}
+
+		_curCD = _curCD < 3 ? _curCD + 1 : 1;
+		swapCD(_curCD);
+	}
+
+	error("openSmkAnim(): File %s not found", name.c_str());
+}
+
+void AnimManager::openSmk(int slot, Common::SeekableReadStream *stream) {
+	_animations[slot] = new NightlongVideoDecoder(_vm->isAmiga());
+
+	if (!_animations[slot]->loadStream(stream)) {
+		warning("Invalid SMK file");
+		closeSmk(slot);
+	} else {
+		_animations[slot]->start();
+	}
+}
+
+void AnimManager::closeSmk(int slot) {
+	delete _animations[slot];
+	_animations[slot] = nullptr;
+}
+
+void AnimManager::smkGoto(int slot, int frame) {
+	if (_animations[slot] == nullptr)
+		return;
+
+	_animations[slot]->forceSeekToFrame(frame);
+}
+
+void AnimManager::smkToggleTrackAudio(int slot, int track, bool on) {
+	if (_animations[slot] == nullptr)
+		return;
+
+	_animations[slot]->muteTrack(track, !on);
+}
+
+void AnimManager::smkToggleAudio(int slot, bool on) {
+	if (_animations[slot] == nullptr)
+		return;
+
+	_animations[slot]->setMute(!on);
+}
+
+int16 AnimManager::smkCurFrame(int slot) {
+	if (_animations[slot] == nullptr)
+		return -1;
+
+	return _animations[slot]->getCurFrame();
+}
+
+void AnimManager::startSmkAnim(uint16 animation) {
+	int slot;
+	uint16 animFlag = _animTab[animation]._flag;
+
+	// choose the buffer to use
+	if (animFlag & SMKANIM_BKG)
+		slot = kSmackerBackground;
+	else if (animFlag & SMKANIM_ICON)
+		slot = kSmackerIcon;
+	else
+		slot = kSmackerAction;
+
+	smkStop(slot);
+
+	_playingAnims[slot] = animation;
+
+	// choose how to open
+	if (slot == kSmackerBackground) {
+		openSmkAnim(kSmackerBackground, _animTab[animation]._name);
+		_bgAnimRestarted = false;
+
+		toggleMuteBgAnim(animation);
+	} else if (slot == kSmackerIcon) {
+		openSmkAnim(kSmackerIcon, _animTab[animation]._name);
+	} else {
+		uint32 st = _vm->readTime();
+		openSmkAnim(kSmackerAction, _animTab[animation]._name);
+		_vm->_nextRefresh += _vm->readTime() - st; // fixup opening time
+	}
+}
+
+void AnimManager::toggleMuteBgAnim(uint16 animation) {
+	const bool area1Shown = _animTab[animation].isAnimAreaShown(1);
+	const bool area2Shown = _animTab[animation].isAnimAreaShown(2);
+	const bool area4Shown = _animTab[animation].isAnimAreaShown(4);
+	NightlongVideoDecoder *decoder = _animations[kSmackerBackground];
+	if (decoder == nullptr)
+		return;
+
+	// Turns off when not needed
+	if (animation == aBKG11 && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG14 && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG1C && _vm->_obj[oFAX17].isFlagExtra()) {
+		_animTab[animation].toggleAnimArea(1, false);
+		decoder->muteTrack(1, true);
+	} else if (animation == aBKG1D && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG22 && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG48 && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG4P && !area1Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG28 && area4Shown)
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG37 && !_vm->_room[_vm->_curRoom].hasExtra())
+		decoder->muteTrack(1, true);
+	else if (animation == aBKG2E && area2Shown)
+		decoder->muteTrack(2, true);
+	else if (animation == aBKG2G && _vm->_dialogMgr->isDialogFinished(556))
+		decoder->muteTrack(2, true);
+	else if (animation == aBKG34 &&                     // If it's BKG 34 and
+			 (_vm->_dialogMgr->isDialogFinished(616) || // if the FMV is already done or
+			  _vm->isObjectVisible(oTUBOT34) ||         // if the whole tube is available or
+			  _vm->isObjectVisible(oTUBOFT34) ||        // if the outside of the tube is available or
+			  _vm->isObjectVisible(oVALVOLAC34)))       // if the valve is closed
+		decoder->muteTrack(2, true);
+}
+
+void AnimManager::smkStop(uint16 slot) {
+	_playingAnims[slot] = 0;
+
+	closeSmk(slot);
+
+	_vm->_lightIcon = 0xFF;
+}
+
+void AnimManager::stopAllSmkAnims() {
+	for (int slot = 0; slot < MAXACTIVEANIM; slot++) {
+		if (_playingAnims[slot])
+			smkStop(slot);
+	}
+}
+
+void AnimManager::startFullMotion() {
+	stopAllSmkAnims();
+
+	_vm->_flagDialogActive = true;
+	_vm->_flagShowCharacter = false;
+
+	_vm->_textStatus = TEXT_OFF;
+	_vm->_inventoryStatus = INV_OFF;
+	_vm->_inventoryCounter = INVENTORY_HIDE;
+
+	_vm->_textMgr->clearTextStack();
+	_vm->_graphicsMgr->clearScreen();
+
+	_vm->_scheduler->resetQueues();
+	_vm->_actor->actorStop();
+	_vm->_graphicsMgr->hideCursor();
+}
+
+void AnimManager::stopFullMotion() {
+	const uint16 curDialog = _vm->_dialogMgr->getCurDialog();
+
+	_vm->_flagDialogActive = false;
+	_vm->_flagDialogMenuActive = false;
+	_vm->_flagSomeoneSpeaks = false;
+	_vm->_lightIcon = 0xFF;
+	_vm->_graphicsMgr->showCursor();
+
+	if (curDialog == dFCRED) {
+		_vm->quitGame();
+		return;
+	}
+
+	if (!((curDialog == dSHOPKEEPER1A) && _vm->_dialogMgr->getCurChoice() == 185)) {
+		if ((curDialog == dF582) || (curDialog == dFLOG) || (curDialog == dINTRO) || (curDialog == dF362) || (curDialog == dC381) || (curDialog == dF381) ||
+			(curDialog == dF491) || ((curDialog == dC581) && !_vm->_dialogMgr->isDialogFinished(886) && _vm->_dialogMgr->isDialogFinished(258)) ||
+			((curDialog == dC5A1) && _vm->_room[kRoom5A].hasExtra()))
+			_vm->_flagShowCharacter = false;
+		else
+			_vm->redrawRoom();
+
+		if (curDialog == dF582)
+			_vm->_soundMgr->stopAllExceptMusic();
+	}
+}
+
+void AnimManager::refreshAnim(int box) {
+	for (int i = 0; i < MAXACTIVEANIM; i++) {
+		if (_playingAnims[i] != 0 && box == BOX_BACKGROUND && i != kSmackerAction) {
+			refreshSmkAnim(_playingAnims[i]);
+		}
+	}
+}
+
+void AnimManager::refreshSmkAnim(uint16 animation) {
+	if (animation == 0)
+		return;
+
+	if (_animTab[animation]._flag & SMKANIM_ICON) {
+		drawSmkIconFrame(_vm->_inventoryRefreshStartIcon, animation);
+	} else if (_animTab[animation]._flag & SMKANIM_BKG) {
+		drawSmkBackgroundFrame(animation);
+		handleEndOfVideo(animation, kSmackerBackground);
+	} else {
+		drawSmkActionFrame();
+		handleEndOfVideo(animation, kSmackerAction);
+	}
+
+	for (int32 i = 0; i < MAXAREA; i++) {
+		if (_animTab[animation].isAnimAreaShown(i + 1) && _animTab[animation]._lim[i].bottom != 0) {
+			_vm->_graphicsMgr->addDirtyRect(_animTab[animation]._lim[i], true);
+		}
+	}
+}
+
+void AnimManager::handleEndOfVideo(int animation, int slot) {
+	const bool isLoopingOrBackground = (_animTab[animation]._flag & SMKANIM_LOOP) || (_animTab[animation]._flag & SMKANIM_BKG);
+
+	if (_animations[slot] == nullptr) {
+		smkStop(slot);
+		return;
+	}
+	if (!_animations[slot]->endOfFrames())
+		return;
+
+	if (!isLoopingOrBackground) {
+		smkStop(slot);
+		_vm->_flagPaintCharacter = true;
+	} else {
+		_animations[slot]->rewind();
+		_vm->_animTypeMgr->init(animation, 0);
+		_bgAnimRestarted = true;
+	}
+}
+
+static bool rectsIntersect(Common::Rect r1, Common::Rect r2) {
+	return (r1.left <= r2.right) && (r1.right >= r2.left) && (r1.top <= r2.bottom) && (r1.bottom >= r2.top);
+}
+
+bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
+	for (int32 i = 0; i < MAXAREA; i++) {
+		const bool intersect = rectsIntersect(_animTab[animation]._lim[i], curRect);
+		if (intersect && !_animTab[animation].isAnimAreaShown(i + 1))
+			return false;
+	}
+
+	return true;
+}
+
+void AnimManager::drawSmkBackgroundFrame(int animation) {
+	NightlongVideoDecoder *smkDecoder = _animations[kSmackerBackground];
+	if (smkDecoder == nullptr)
+		return;
+	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+	if (!frame)
+		return;
+
+	const Common::Rect *lastRect = smkDecoder->getNextDirtyRect();
+	const byte *palette = smkDecoder->getPalette();
+
+	if (smkDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
+		_vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
+	} else {
+		while (lastRect) {
+			if (smkDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
+				Graphics::Surface anim = frame->getSubArea(*lastRect);
+				_vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
+			}
+
+			lastRect = smkDecoder->getNextDirtyRect();
+		}
+	}
+}
+
+void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
+	NightlongVideoDecoder *smkDecoder = _animations[kSmackerIcon];
+	if (smkDecoder == nullptr)
+		return;
+
+	int stx = ICONMARGSX;
+	uint a;
+	for (a = 0; a < ICONSHOWN; ++a) {
+		if (a + startIcon >= _vm->_inventory.size())
+			break;
+
+		if (_vm->_inventory[a + startIcon] == iconNum - FIRST_INV_ITEM + 1) {
+			stx = a * ICONDX + ICONMARGSX;
+			break;
+		}
+	}
+
+	if (a == ICONSHOWN)
+		return;
+
+	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+	_vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, smkDecoder->getPalette());
+
+	if (smkDecoder->endOfVideo())
+		smkDecoder->rewind();
+}
+
+void AnimManager::drawSmkActionFrame() {
+	NightlongVideoDecoder *smkDecoder = _animations[kSmackerAction];
+	if (smkDecoder == nullptr)
+		return;
+	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+	if (!frame)
+		return;
+
+	const byte *palette = smkDecoder->getPalette();
+
+	if (smkDecoder->getCurFrame() == 0) {
+		_animRect = *smkDecoder->getNextDirtyRect();
+	}
+
+	if (_animRect.width() > 0 && _animRect.height() > 0) {
+		Graphics::Surface anim = frame->getSubArea(_animRect);
+		_vm->_graphicsMgr->blitToScreenBuffer(&anim, _animRect.left, _animRect.top + TOP, palette, false);
+		_vm->_graphicsMgr->addDirtyRect(_animRect, true);
+	}
+}
+
+void AnimManager::swapCD(int cd) {
+	Common::String animFileName = Common::String::format("nlanim.cd%d", cd);
+	for (uint8 i = 0; i < MAXACTIVEANIM; ++i) {
+		_animFile[i].close();
+		_animFile[i].open(_vm, animFileName);
+	}
+}
+
+void AnimManager::syncGameStream(Common::Serializer &ser) {
+	for (int i = 0; i < MAXANIM; i++) {
+		SAnim *cur = &_animTab[i];
+		ser.syncBytes((byte *)cur->_name, 14);
+		ser.syncAsUint16LE(cur->_flag);
+		for (uint8 j = 0; j < MAXAREA; ++j) {
+			ser.syncAsUint16LE(cur->_lim[j].left);
+			ser.syncAsUint16LE(cur->_lim[j].top);
+			ser.syncAsUint16LE(cur->_lim[j].right);
+			ser.syncAsUint16LE(cur->_lim[j].bottom);
+		}
+		ser.syncAsByte(cur->_nbox);
+		ser.skip(1, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
+		for (uint8 j = 0; j < MAXATFRAME; ++j) {
+			ser.syncAsByte(cur->_atFrame[j]._type);
+			ser.syncAsByte(cur->_atFrame[j]._area);
+			ser.syncAsUint16LE(cur->_atFrame[j]._numFrame);
+			ser.syncAsUint16LE(cur->_atFrame[j]._index);
+		}
+	}
+
+	patchAnimTab();
+}
+
+void AnimManager::loadAnimTab(Common::SeekableReadStreamEndian *stream) {
+	for (uint16 i = 0; i < MAXANIM; ++i) {
+		stream->read(&_animTab[i]._name, 14);
+
+		_animTab[i]._flag = stream->readUint16();
+
+		for (uint8 j = 0; j < MAXAREA; ++j) {
+			_animTab[i]._lim[j].left = stream->readUint16();
+			_animTab[i]._lim[j].top = stream->readUint16();
+			_animTab[i]._lim[j].right = stream->readUint16();
+			_animTab[i]._lim[j].bottom = stream->readUint16();
+		}
+
+		_animTab[i]._nbox = stream->readByte();
+		stream->readByte(); // Padding
+
+		for (uint8 j = 0; j < MAXATFRAME; ++j) {
+			_animTab[i]._atFrame[j]._type = stream->readByte();
+			_animTab[i]._atFrame[j]._area = stream->readByte();
+			_animTab[i]._atFrame[j]._numFrame = stream->readUint16();
+			_animTab[i]._atFrame[j]._index = stream->readUint16();
+		}
+	}
+
+	patchAnimTab();
+}
+
+void AnimManager::patchAnimTab() {
+	_animTab[aBKG28]._lim[3].left = 308;  // Patch the brazier animation rect in kRoom28 - bug #12628
+	_animTab[aBKG35]._lim[0].right = 200; // Patch the terrorist animation rect in kRoom35
+}
+
+} // End of namespace Trecision
diff --git a/engines/trecision/animmanager.h b/engines/trecision/animmanager.h
new file mode 100644
index 00000000000..d6bf04108b7
--- /dev/null
+++ b/engines/trecision/animmanager.h
@@ -0,0 +1,106 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef TRECISION_ANIMMANAGER_H
+#define TRECISION_ANIMMANAGER_H
+
+#include "trecision/fastfile.h"
+#include "trecision/struct.h"
+
+namespace Trecision {
+
+#define MAXANIM 750
+#define MAXACTIVEANIM 3
+
+// SMACKER ANIMATION FLAGS
+#define SMKANIM_BKG 1
+#define SMKANIM_ICON 2
+#define SMKANIM_LOOP 4
+#define SMKANIM_OLD 8
+#define SMKANIM_ON 16
+
+enum SmackerType {
+	kSmackerBackground = 0, // Scene background animations
+	kSmackerAction = 1,     // Main character action animations
+	kSmackerIcon = 2        // Smacker inventory animations
+};
+
+class TrecisionEngine;
+class NightlongVideoDecoder;
+
+class AnimManager {
+public:
+	AnimManager(TrecisionEngine *vm);
+	~AnimManager();
+
+private:
+	TrecisionEngine *_vm;
+
+	NightlongVideoDecoder *_animations[MAXACTIVEANIM];
+	uint16 _playingAnims[MAXACTIVEANIM];
+
+	FastFile _animFile[MAXACTIVEANIM]; // nlanim.cd1 / nlanim.cd2 / nlanim.cd3
+	int _curCD;
+	bool _bgAnimRestarted;
+
+	void openSmk(int slot, Common::SeekableReadStream *stream);
+	void openSmkAnim(int slot, const Common::String &name);
+	void toggleMuteBgAnim(uint16 animation);
+	void closeSmk(int slot);
+	void drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen);
+	void drawFrameSubtitles(Graphics::Surface *surface, int frameNum);
+	void setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame);
+	void refreshSmkAnim(uint16 animation);
+	void handleEndOfVideo(int animation, int slot);
+	bool shouldShowAnim(int animation, Common::Rect curRect);
+
+	void drawSmkBackgroundFrame(int animation);
+	void drawSmkIconFrame(uint16 startIcon, uint16 iconNum);
+	void drawSmkActionFrame();
+	void swapCD(int cd);
+	void patchAnimTab();
+
+public:
+	Common::Rect _animRect;
+	SAnim _animTab[MAXANIM];
+
+	void smkGoto(int slot, int frame);
+	void smkToggleAudio(int slot, bool on);
+	void smkToggleTrackAudio(int slot, int track, bool on);
+	int16 smkCurFrame(int slot);
+	void smkStop(uint16 slot);
+	void refreshActionAnimation() { refreshSmkAnim(_playingAnims[kSmackerAction]); }
+	bool isActionActive() const { return _playingAnims[kSmackerAction] != 0; }
+	void playMovie(const Common::String &filename, int startFrame = 0, int endFrame = -1, bool singleChoice = false);
+	void startFullMotion();
+	void stopFullMotion();
+
+	void refreshAnim(int box);
+	void startSmkAnim(uint16 animation);
+	void stopAllSmkAnims();
+
+	void syncGameStream(Common::Serializer &ser);
+	void loadAnimTab(Common::SeekableReadStreamEndian *stream);
+};
+
+} // End of namespace Trecision
+#endif
+
diff --git a/engines/trecision/anim.cpp b/engines/trecision/animtype.cpp
similarity index 99%
rename from engines/trecision/anim.cpp
rename to engines/trecision/animtype.cpp
index c15a56d1e9e..3e5a094335f 100644
--- a/engines/trecision/anim.cpp
+++ b/engines/trecision/animtype.cpp
@@ -19,7 +19,8 @@
  *
  */
 
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
 #include "trecision/dialog.h"
 #include "trecision/logic.h"
 #include "trecision/pathfinding3d.h"
diff --git a/engines/trecision/anim.h b/engines/trecision/animtype.h
similarity index 96%
rename from engines/trecision/anim.h
rename to engines/trecision/animtype.h
index 099e2518d89..f99ce70161b 100644
--- a/engines/trecision/anim.h
+++ b/engines/trecision/animtype.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef TRECISION_ANIM_H
-#define TRECISION_ANIM_H
+#ifndef TRECISION_ANIMTYPE_H
+#define TRECISION_ANIMTYPE_H
 
 #include "trecision/struct.h"
 
diff --git a/engines/trecision/dialog.cpp b/engines/trecision/dialog.cpp
index 3507c02b486..d548e9d062e 100644
--- a/engines/trecision/dialog.cpp
+++ b/engines/trecision/dialog.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/defines.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
diff --git a/engines/trecision/graphics.cpp b/engines/trecision/graphics.cpp
index 3a476223dad..2e454c3a2bf 100644
--- a/engines/trecision/graphics.cpp
+++ b/engines/trecision/graphics.cpp
@@ -27,7 +27,8 @@
 #include "graphics/surface.h"
 
 #include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
 #include "trecision/defines.h"
 #include "trecision/graphics.h"
 #include "trecision/pathfinding3d.h"
diff --git a/engines/trecision/inventory.cpp b/engines/trecision/inventory.cpp
index e57123f24cd..e663950cd6b 100644
--- a/engines/trecision/inventory.cpp
+++ b/engines/trecision/inventory.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/defines.h"
 #include "trecision/graphics.h"
 #include "trecision/logic.h"
diff --git a/engines/trecision/logic.cpp b/engines/trecision/logic.cpp
index 00ed0be7ee3..e2f87ae6971 100644
--- a/engines/trecision/logic.cpp
+++ b/engines/trecision/logic.cpp
@@ -22,6 +22,7 @@
 #include "common/config-manager.h"
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/defines.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
diff --git a/engines/trecision/module.mk b/engines/trecision/module.mk
index e149ef97277..95bccab4adf 100644
--- a/engines/trecision/module.mk
+++ b/engines/trecision/module.mk
@@ -3,7 +3,8 @@ MODULE := engines/trecision
 MODULE_OBJS = \
 	console.o \
 	actor.o \
-	anim.o \
+	animmanager.o \
+	animtype.o \
 	dialog.o \
 	fastfile.o \
 	graphics.o \
diff --git a/engines/trecision/pathfinding3d.cpp b/engines/trecision/pathfinding3d.cpp
index 4e740376c6e..4f291183b2c 100644
--- a/engines/trecision/pathfinding3d.cpp
+++ b/engines/trecision/pathfinding3d.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
 #include "trecision/pathfinding3d.h"
 #include "trecision/sound.h"
 #include "trecision/trecision.h"
diff --git a/engines/trecision/renderer3d.cpp b/engines/trecision/renderer3d.cpp
index 4b8095b829d..fcff0f594e6 100644
--- a/engines/trecision/renderer3d.cpp
+++ b/engines/trecision/renderer3d.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
 #include "trecision/graphics.h"
 #include "trecision/renderer3d.h"
 #include "trecision/trecision.h"
diff --git a/engines/trecision/resource.cpp b/engines/trecision/resource.cpp
index d9f46a1e8d2..2a88873bb18 100644
--- a/engines/trecision/resource.cpp
+++ b/engines/trecision/resource.cpp
@@ -29,6 +29,7 @@
 #include "gui/saveload.h"
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/defines.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
diff --git a/engines/trecision/saveload.cpp b/engines/trecision/saveload.cpp
index 56e52d2c994..fbcc662c3ac 100644
--- a/engines/trecision/saveload.cpp
+++ b/engines/trecision/saveload.cpp
@@ -25,6 +25,7 @@
 #include "common/translation.h"
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
 #include "trecision/logic.h"
diff --git a/engines/trecision/script.cpp b/engines/trecision/script.cpp
index 599902cf16f..cc700fc0b63 100644
--- a/engines/trecision/script.cpp
+++ b/engines/trecision/script.cpp
@@ -23,7 +23,8 @@
 #include "graphics/scaler.h"
 
 #include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
 #include "trecision/defines.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
diff --git a/engines/trecision/text.cpp b/engines/trecision/text.cpp
index f7643399c31..6682cc04824 100644
--- a/engines/trecision/text.cpp
+++ b/engines/trecision/text.cpp
@@ -23,6 +23,7 @@
 #include "common/str.h"
 
 #include "trecision/actor.h"
+#include "trecision/animmanager.h"
 #include "trecision/graphics.h"
 #include "trecision/sound.h"
 #include "trecision/trecision.h"
diff --git a/engines/trecision/trecision.cpp b/engines/trecision/trecision.cpp
index 63f2ac825e6..9594710a9b1 100644
--- a/engines/trecision/trecision.cpp
+++ b/engines/trecision/trecision.cpp
@@ -28,7 +28,8 @@
 #include "common/fs.h"
 #include "common/str.h"
 
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
 #include "trecision/actor.h"
 #include "trecision/console.h"
 #include "trecision/defines.h"
diff --git a/engines/trecision/video.cpp b/engines/trecision/video.cpp
index 14bd91d72c3..727d0f72339 100644
--- a/engines/trecision/video.cpp
+++ b/engines/trecision/video.cpp
@@ -25,8 +25,10 @@
 #include "common/scummsys.h"
 #include "common/system.h"
 
+#include "audio/decoders/raw.h"
+
 #include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
 #include "trecision/defines.h"
 #include "trecision/dialog.h"
 #include "trecision/graphics.h"
@@ -37,6 +39,160 @@
 
 namespace Trecision {
 
+NightlongVideoDecoder::NightlongVideoDecoder(bool isAmiga) {
+	_isAmiga = isAmiga;
+	_smkDecoder = !isAmiga ? new NightlongSmackerDecoder() : nullptr;
+	_mixer = g_system->getMixer();
+}
+
+NightlongVideoDecoder::~NightlongVideoDecoder() {
+	delete _smkDecoder;
+
+	if (_mixer->isSoundHandleActive(_amigaSoundHandle))
+		_mixer->stopHandle(_amigaSoundHandle);
+}
+
+bool NightlongVideoDecoder::loadFile(const Common::Path &filename) {
+	if (!_isAmiga)
+		return _smkDecoder->loadFile(filename);
+	else {
+		// TODO: Amiga video format
+
+		// Load the video's audio track
+		Common::File *stream = new Common::File();
+		Common::String file = filename.toString();
+		stream->open("a" + file);
+
+		if (stream->isOpen()) {
+			Audio::SeekableAudioStream *sound = Audio::makeRawStream(stream, 11025, 0, DisposeAfterUse::YES);
+
+			_mixer->playStream(
+				Audio::Mixer::kSFXSoundType,
+				&_amigaSoundHandle,
+				sound);
+
+			return true;
+		} else {
+			delete stream;
+			return false;
+		}	
+	}
+}
+
+bool NightlongVideoDecoder::loadStream(Common::SeekableReadStream *stream) {
+	if (!_isAmiga)
+		return _smkDecoder->loadStream(stream);
+	else
+		return false;	// TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::muteTrack(uint track, bool mute) {
+	if (!_isAmiga)
+		_smkDecoder->muteTrack(track, mute);
+	// TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::setMute(bool mute) {
+	if (!_isAmiga)
+		_smkDecoder->setMute(mute);
+	// TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::forceSeekToFrame(uint frame) {
+	if (!_isAmiga)
+		return _smkDecoder->forceSeekToFrame(frame);
+	else
+		return false;	// TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::endOfFrames() const {
+	if (!_isAmiga)
+		return _smkDecoder->endOfFrames();
+	else
+		return !_mixer->isSoundHandleActive(_amigaSoundHandle);	// HACK, since we only play the audio for now
+}
+
+int NightlongVideoDecoder::getCurFrame() const {
+	if (!_isAmiga)
+		return _smkDecoder->getCurFrame();
+	else
+		return 0;	// TODO: Amiga videos
+}
+
+uint16 NightlongVideoDecoder::getWidth() const {
+	if (!_isAmiga)
+		return _smkDecoder->getWidth();
+	else
+		return 0;	// TODO: Amiga videos
+}
+
+uint16 NightlongVideoDecoder::getHeight() const {
+	if (!_isAmiga)
+		return _smkDecoder->getHeight();
+	else
+		return 0;	// TODO: Amiga videos
+}
+
+const Graphics::Surface *NightlongVideoDecoder::decodeNextFrame() {
+	if (!_isAmiga)
+		return _smkDecoder->decodeNextFrame();
+	else
+		return nullptr;	// TODO: Amiga videos
+}
+
+uint32 NightlongVideoDecoder::getFrameCount() const {
+	if (!_isAmiga)
+		return _smkDecoder->getFrameCount();
+	else
+		return 10;	// TODO: Amiga videos. Anything > 1 to keep playing till the audio is done
+}
+
+const byte *NightlongVideoDecoder::getPalette() {
+	if (!_isAmiga)
+		return _smkDecoder->getPalette();
+	else
+		return nullptr;	// TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::start() {
+	if (!_isAmiga)
+		_smkDecoder->start();
+	// TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::rewind() {
+	if (!_isAmiga)
+		_smkDecoder->rewind();
+	// TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::needsUpdate() const {
+	if (!_isAmiga)
+		return _smkDecoder->needsUpdate();
+	else
+		return false;	// TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::setEndFrame(uint frame) {
+	if (!_isAmiga)
+		_smkDecoder->setEndFrame(frame);
+	// TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::endOfVideo() const {
+	if (!_isAmiga)
+		return _smkDecoder->endOfVideo();
+	else
+		return false;	// TODO: Amiga videos
+}
+
+const Common::Rect *NightlongVideoDecoder::getNextDirtyRect() {
+	if (!_isAmiga)
+		return _smkDecoder->getNextDirtyRect();
+	else
+		return nullptr;	// TODO: Amiga videos
+}
+
 bool NightlongSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
 	if (!SmackerDecoder::loadStream(stream))
 		return false;
@@ -115,509 +271,4 @@ bool NightlongSmackerDecoder::endOfFrames() const {
 	return getCurFrame() >= (int32)getFrameCount() - 1;
 }
 
-AnimManager::AnimManager(TrecisionEngine *vm) : _vm(vm) {
-	for (int i = 0; i < MAXSMACK; ++i) {
-		_smkAnims[i] = nullptr;
-		_playingAnims[i] = 0;
-	}
-
-	for (int i = 0; i < MAXANIM; ++i) {
-		_animTab[i]._flag = 0;
-		_animTab[i]._name[0] = '\0';
-	}
-
-	_curCD = 1;
-	swapCD(_curCD);
-
-	_bgAnimRestarted = false;
-}
-
-AnimManager::~AnimManager() {
-	for (int i = 0; i < MAXSMACK; ++i) {
-		delete _smkAnims[i];
-		_smkAnims[i] = nullptr;
-		_animFile[i].close();
-	}
-}
-
-void AnimManager::playMovie(const Common::String &filename, int startFrame, int endFrame, bool singleChoice) {
-	NightlongSmackerDecoder *smkDecoder = new NightlongSmackerDecoder;
-
-	if (!smkDecoder->loadFile(filename)) {
-		warning("playMovie: File %s not found", filename.c_str());
-		delete smkDecoder;
-		_vm->_dialogMgr->afterChoice();
-		return;
-	}
-
-	Common::Event event;
-	bool skipVideo = false;
-	uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
-	uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
-	_vm->_drawText._text.clear();
-
-	smkDecoder->start();
-
-	// WORKAROUND: If the video has a single choice, and it starts from
-	// the beginning, ignore the calculated end frame and play all of it
-	if (singleChoice && startFrame < 10 && endFrame < (int)smkDecoder->getFrameCount() - 1)
-		endFrame = smkDecoder->getFrameCount() - 1;
-
-	setVideoRange(smkDecoder, startFrame, endFrame);
-
-	while (!_vm->shouldQuit() && startFrame != endFrame && !smkDecoder->endOfVideo() && !skipVideo) {
-		if (smkDecoder->needsUpdate()) {
-			drawFrame(smkDecoder, x, y, true);
-		}
-
-		while (_vm->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYUP && event.kbd.keycode == Common::KEYCODE_ESCAPE)
-				skipVideo = true;
-		}
-
-		g_system->delayMillis(10);
-	}
-
-	delete smkDecoder;
-
-	_vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
-	_vm->freeKey();
-	_vm->_dialogMgr->afterChoice();
-}
-
-void AnimManager::setVideoRange(NightlongSmackerDecoder *smkDecoder, int &startFrame, int &endFrame) {
-	// Trecision starts at 1 but ScummVM starts at 0
-	startFrame = CLIP<int32>(startFrame - 1, 0, smkDecoder->getFrameCount() - 1);
-	endFrame = CLIP<int32>(endFrame - 1, 0, smkDecoder->getFrameCount() - 1);
-
-	// If choices are attached
-	if (startFrame > 0 && startFrame > smkDecoder->getCurFrame()) {
-		smkDecoder->forceSeekToFrame(startFrame - 1);
-	}
-	smkDecoder->setEndFrame(endFrame);
-}
-
-void AnimManager::drawFrame(NightlongSmackerDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen) {
-	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
-	if (frame) {
-		Graphics::Surface *frame16 = frame->convertTo(g_system->getScreenFormat(), smkDecoder->getPalette());
-		drawFrameSubtitles(frame16, smkDecoder->getCurFrame());
-		g_system->copyRectToScreen(frame16->getPixels(), frame16->pitch, x, y, frame16->w, frame16->h);
-		frame16->free();
-		delete frame16;
-
-		if (updateScreen)
-			_vm->_system->updateScreen();
-	}
-}
-
-void AnimManager::drawFrameSubtitles(Graphics::Surface *surface, int frameNum) {
-	if (!ConfMan.getBool("subtitles"))
-		return;
-
-	_vm->_dialogMgr->dialogHandler(frameNum);
-	if (_vm->_drawText._text.empty())
-		return;
-
-	// Subtitles can be placed in different coordinates in the video,
-	// which are set inside dialogHandler(), but are then reset to
-	// fixed coordinates
-	_vm->_drawText._rect.left = 20;
-	_vm->_drawText._rect.top = 380 - TOP;
-	_vm->_drawText._rect.setWidth(MAXX - 40);
-	_vm->_drawText._rect.setHeight(_vm->_drawText.calcHeight(_vm));
-	_vm->_drawText._subtitleRect = Common::Rect(MAXX, MAXY);
-	_vm->_drawText.draw(_vm, false, surface);
-}
-
-void AnimManager::openSmkAnim(int slot, const Common::String &name) {
-	for (int i = 0; i < 3; i++) {
-		// Open the animation, or swap the 3 CDs to find it
-		if (_animFile[slot].hasFile(name)) {
-			openSmk(slot, _animFile[slot].createReadStreamForMember(name));
-			return;
-		}
-
-		_curCD = _curCD < 3 ? _curCD + 1 : 1;
-		swapCD(_curCD);
-	}
-
-	error("openSmkAnim(): File %s not found", name.c_str());
-}
-
-void AnimManager::openSmk(int slot, Common::SeekableReadStream *stream) {
-	_smkAnims[slot] = new NightlongSmackerDecoder();
-
-	if (!_smkAnims[slot]->loadStream(stream)) {
-		warning("Invalid SMK file");
-		closeSmk(slot);
-	} else {
-		_smkAnims[slot]->start();
-	}
-}
-
-void AnimManager::closeSmk(int slot) {
-	delete _smkAnims[slot];
-	_smkAnims[slot] = nullptr;
-}
-
-void AnimManager::smkGoto(int slot, int frame) {
-	if (_smkAnims[slot] == nullptr)
-		return;
-
-	_smkAnims[slot]->forceSeekToFrame(frame);
-}
-
-void AnimManager::smkToggleTrackAudio(int slot, int track, bool on) {
-	if (_smkAnims[slot] == nullptr)
-		return;
-
-	_smkAnims[slot]->muteTrack(track, !on);
-}
-
-void AnimManager::smkToggleAudio(int slot, bool on) {
-	if (_smkAnims[slot] == nullptr)
-		return;
-
-	_smkAnims[slot]->setMute(!on);
-}
-
-int16 AnimManager::smkCurFrame(int slot) {
-	if (_smkAnims[slot] == nullptr)
-		return -1;
-
-	return _smkAnims[slot]->getCurFrame();
-}
-
-void AnimManager::startSmkAnim(uint16 animation) {
-	int slot;
-	uint16 animFlag = _animTab[animation]._flag;
-
-	// choose the buffer to use
-	if (animFlag & SMKANIM_BKG)
-		slot = kSmackerBackground;
-	else if (animFlag & SMKANIM_ICON)
-		slot = kSmackerIcon;
-	else
-		slot = kSmackerAction;
-
-	smkStop(slot);
-
-	_playingAnims[slot] = animation;
-
-	// choose how to open
-	if (slot == kSmackerBackground) {
-		openSmkAnim(kSmackerBackground, _animTab[animation]._name);
-		_bgAnimRestarted = false;
-
-		toggleMuteBgAnim(animation);
-	} else if (slot == kSmackerIcon) {
-		openSmkAnim(kSmackerIcon, _animTab[animation]._name);
-	} else {
-		uint32 st = _vm->readTime();
-		openSmkAnim(kSmackerAction, _animTab[animation]._name);
-		_vm->_nextRefresh += _vm->readTime() - st; // fixup opening time
-	}
-}
-
-void AnimManager::toggleMuteBgAnim(uint16 animation) {
-	const bool area1Shown = _animTab[animation].isAnimAreaShown(1);
-	const bool area2Shown = _animTab[animation].isAnimAreaShown(2);
-	const bool area4Shown = _animTab[animation].isAnimAreaShown(4);
-	NightlongSmackerDecoder *decoder = _smkAnims[kSmackerBackground];
-	if (decoder == nullptr)
-		return;
-
-	// Turns off when not needed
-	if (animation == aBKG11 && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG14 && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG1C && _vm->_obj[oFAX17].isFlagExtra()) {
-		_animTab[animation].toggleAnimArea(1, false);
-		decoder->muteTrack(1, true);
-	} else if (animation == aBKG1D && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG22 && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG48 && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG4P && !area1Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG28 && area4Shown)
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG37 && !_vm->_room[_vm->_curRoom].hasExtra())
-		decoder->muteTrack(1, true);
-	else if (animation == aBKG2E && area2Shown)
-		decoder->muteTrack(2, true);
-	else if (animation == aBKG2G && _vm->_dialogMgr->isDialogFinished(556))
-		decoder->muteTrack(2, true);
-	else if (animation == aBKG34 &&                                   // If it's BKG 34 and
-			 (_vm->_dialogMgr->isDialogFinished(616) || // if the FMV is already done or
-			  _vm->isObjectVisible(oTUBOT34) ||                       // if the whole tube is available or
-			  _vm->isObjectVisible(oTUBOFT34) ||                      // if the outside of the tube is available or
-			  _vm->isObjectVisible(oVALVOLAC34)))                     // if the valve is closed
-		decoder->muteTrack(2, true);
-}
-
-void AnimManager::smkStop(uint16 slot) {
-	_playingAnims[slot] = 0;
-
-	closeSmk(slot);
-
-	_vm->_lightIcon = 0xFF;
-}
-
-void AnimManager::stopAllSmkAnims() {
-	for (int slot = 0; slot < MAXSMACK; slot++) {
-		if (_playingAnims[slot])
-			smkStop(slot);
-	}
-}
-
-void AnimManager::startFullMotion() {
-	stopAllSmkAnims();
-
-	_vm->_flagDialogActive = true;
-	_vm->_flagShowCharacter = false;
-
-	_vm->_textStatus = TEXT_OFF;
-	_vm->_inventoryStatus = INV_OFF;
-	_vm->_inventoryCounter = INVENTORY_HIDE;
-
-	_vm->_textMgr->clearTextStack();
-	_vm->_graphicsMgr->clearScreen();
-
-	_vm->_scheduler->resetQueues();
-	_vm->_actor->actorStop();
-	_vm->_graphicsMgr->hideCursor();
-}
-
-void AnimManager::stopFullMotion() {
-	const uint16 curDialog = _vm->_dialogMgr->getCurDialog();
-
-	_vm->_flagDialogActive = false;
-	_vm->_flagDialogMenuActive = false;
-	_vm->_flagSomeoneSpeaks = false;
-	_vm->_lightIcon = 0xFF;
-	_vm->_graphicsMgr->showCursor();
-
-	if (curDialog == dFCRED) {
-		_vm->quitGame();
-		return;
-	}
-
-	if (!((curDialog == dSHOPKEEPER1A) && _vm->_dialogMgr->getCurChoice() == 185)) {
-		if ((curDialog == dF582) || (curDialog == dFLOG) || (curDialog == dINTRO) || (curDialog == dF362) || (curDialog == dC381) || (curDialog == dF381) ||
-		    (curDialog == dF491) || ((curDialog == dC581) && !_vm->_dialogMgr->isDialogFinished(886) && _vm->_dialogMgr->isDialogFinished(258)) ||
-		    ((curDialog == dC5A1) && _vm->_room[kRoom5A].hasExtra()))
-			_vm->_flagShowCharacter = false;
-		else
-			_vm->redrawRoom();
-
-		if (curDialog == dF582)
-			_vm->_soundMgr->stopAllExceptMusic();
-	}
-}
-
-void AnimManager::refreshAnim(int box) {
-	for (int i = 0; i < MAXSMACK; i++) {
-		if (_playingAnims[i] != 0 && box == BOX_BACKGROUND && i != kSmackerAction) {
-			refreshSmkAnim(_playingAnims[i]);
-		}
-	}
-}
-
-void AnimManager::refreshSmkAnim(uint16 animation) {
-	if (animation == 0)
-		return;
-
-	if (_animTab[animation]._flag & SMKANIM_ICON) {
-		drawSmkIconFrame(_vm->_inventoryRefreshStartIcon, animation);
-	} else if (_animTab[animation]._flag & SMKANIM_BKG) {
-		drawSmkBackgroundFrame(animation);
-		handleEndOfVideo(animation, kSmackerBackground);
-	} else {
-		drawSmkActionFrame();
-		handleEndOfVideo(animation, kSmackerAction);
-	}
-
-	for (int32 i = 0; i < MAXAREA; i++) {
-		if (_animTab[animation].isAnimAreaShown(i + 1) && _animTab[animation]._lim[i].bottom != 0) {
-			_vm->_graphicsMgr->addDirtyRect(_animTab[animation]._lim[i], true);
-		}
-	}
-}
-
-void AnimManager::handleEndOfVideo(int animation, int slot) {
-	const bool isLoopingOrBackground = (_animTab[animation]._flag & SMKANIM_LOOP) || (_animTab[animation]._flag & SMKANIM_BKG);
-
-	if (_smkAnims[slot] == nullptr) {
-		smkStop(slot);
-		return;
-	}
-	if (!_smkAnims[slot]->endOfFrames())
-		return;
-	
-	if (!isLoopingOrBackground) {
-		smkStop(slot);
-		_vm->_flagPaintCharacter = true;
-	} else {
-		_smkAnims[slot]->rewind();
-		_vm->_animTypeMgr->init(animation, 0);
-		_bgAnimRestarted = true;
-	}
-}
-
-static bool rectsIntersect(Common::Rect r1, Common::Rect r2) {
-	return (r1.left <= r2.right) && (r1.right >= r2.left) && (r1.top <= r2.bottom) && (r1.bottom >= r2.top);
-}
-
-bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
-	for (int32 i = 0; i < MAXAREA; i++) {
-		const bool intersect = rectsIntersect(_animTab[animation]._lim[i], curRect);
-		if (intersect && !_animTab[animation].isAnimAreaShown(i + 1))
-			return false;
-	}
-
-	return true;
-}
-
-void AnimManager::drawSmkBackgroundFrame(int animation) {
-	NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerBackground];
-	if (smkDecoder == nullptr)
-		return;
-	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
-	if (!frame)
-		return;
-
-	const Common::Rect *lastRect = smkDecoder->getNextDirtyRect();
-	const byte *palette = smkDecoder->getPalette();
-
-	if (smkDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
-		_vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
-	} else {
-		while (lastRect) {
-			if (smkDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
-				Graphics::Surface anim = frame->getSubArea(*lastRect);
-				_vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
-			}
-
-			lastRect = smkDecoder->getNextDirtyRect();
-		}
-	}
-}
-
-void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
-	NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerIcon];
-	if (smkDecoder == nullptr)
-		return;
-
-	int stx = ICONMARGSX;
-	uint a;
-	for (a = 0; a < ICONSHOWN; ++a) {
-		if (a + startIcon >= _vm->_inventory.size())
-			break;
-
-		if (_vm->_inventory[a + startIcon] == iconNum - FIRST_INV_ITEM + 1) {
-			stx = a * ICONDX + ICONMARGSX;
-			break;
-		}
-	}
-
-	if (a == ICONSHOWN)
-		return;
-
-	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
-	_vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, smkDecoder->getPalette());
-
-	if (smkDecoder->endOfVideo())
-		smkDecoder->rewind();
-}
-
-void AnimManager::drawSmkActionFrame() {
-	NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerAction];
-	if (smkDecoder == nullptr)
-		return;
-	const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
-	if (!frame)
-		return;
-
-	const byte *palette = smkDecoder->getPalette();
-
-	if (smkDecoder->getCurFrame() == 0) {
-		_animRect = *smkDecoder->getNextDirtyRect();
-	}
-
-	if (_animRect.width() > 0 && _animRect.height() > 0) {
-		Graphics::Surface anim = frame->getSubArea(_animRect);
-		_vm->_graphicsMgr->blitToScreenBuffer(&anim, _animRect.left, _animRect.top + TOP, palette, false);
-		_vm->_graphicsMgr->addDirtyRect(_animRect, true);
-	}
-}
-
-void AnimManager::swapCD(int cd) {
-	Common::String animFileName = Common::String::format("nlanim.cd%d", cd);
-	for (uint8 i = 0; i < MAXSMACK; ++i) {
-		_animFile[i].close();
-		_animFile[i].open(_vm, animFileName);
-	}
-}
-
-void AnimManager::syncGameStream(Common::Serializer &ser) {
-	for (int i = 0; i < MAXANIM; i++) {
-		SAnim *cur = &_animTab[i];
-		ser.syncBytes((byte *)cur->_name, 14);
-		ser.syncAsUint16LE(cur->_flag);
-		for (uint8 j = 0; j < MAXAREA; ++j) {
-			ser.syncAsUint16LE(cur->_lim[j].left);
-			ser.syncAsUint16LE(cur->_lim[j].top);
-			ser.syncAsUint16LE(cur->_lim[j].right);
-			ser.syncAsUint16LE(cur->_lim[j].bottom);
-		}
-		ser.syncAsByte(cur->_nbox);
-		ser.skip(1, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
-		for (uint8 j = 0; j < MAXATFRAME; ++j) {
-			ser.syncAsByte(cur->_atFrame[j]._type);
-			ser.syncAsByte(cur->_atFrame[j]._area);
-			ser.syncAsUint16LE(cur->_atFrame[j]._numFrame);
-			ser.syncAsUint16LE(cur->_atFrame[j]._index);
-		}
-	}
-
-	patchAnimTab();
-}
-
-void AnimManager::loadAnimTab(Common::SeekableReadStreamEndian *stream) {
-	for (uint16 i = 0; i < MAXANIM; ++i) {
-		stream->read(&_animTab[i]._name, 14);
-
-		_animTab[i]._flag = stream->readUint16();
-
-		for (uint8 j = 0; j < MAXAREA; ++j) {
-			_animTab[i]._lim[j].left = stream->readUint16();
-			_animTab[i]._lim[j].top = stream->readUint16();
-			_animTab[i]._lim[j].right = stream->readUint16();
-			_animTab[i]._lim[j].bottom = stream->readUint16();
-		}
-
-		_animTab[i]._nbox = stream->readByte();
-		stream->readByte(); // Padding
-
-		for (uint8 j = 0; j < MAXATFRAME; ++j) {
-			_animTab[i]._atFrame[j]._type = stream->readByte();
-			_animTab[i]._atFrame[j]._area = stream->readByte();
-			_animTab[i]._atFrame[j]._numFrame = stream->readUint16();
-			_animTab[i]._atFrame[j]._index = stream->readUint16();
-		}
-	}
-
-	patchAnimTab();
-}
-
-void AnimManager::patchAnimTab() {
-	_animTab[aBKG28]._lim[3].left = 308; // Patch the brazier animation rect in kRoom28 - bug #12628
-	_animTab[aBKG35]._lim[0].right = 200; // Patch the terrorist animation rect in kRoom35
-}
-
 } // namespace Trecision
diff --git a/engines/trecision/video.h b/engines/trecision/video.h
index c9aabf2a9cd..19c9b1bdd5f 100644
--- a/engines/trecision/video.h
+++ b/engines/trecision/video.h
@@ -30,22 +30,6 @@
 
 namespace Trecision {
 
-#define MAXANIM  750
-#define MAXSMACK 3
-
-// SMACKER ANIMATION FLAGS
-#define SMKANIM_BKG 1
-#define SMKANIM_ICON 2
-#define SMKANIM_LOOP 4
-#define SMKANIM_OLD 8
-#define SMKANIM_ON 16
-
-enum SmackerType {
-	kSmackerBackground = 0,		// Scene background animations
-	kSmackerAction = 1,			// Main character action animations
-	kSmackerIcon = 2			// Smacker inventory animations
-};
-
 class TrecisionEngine;
 
 class NightlongSmackerDecoder : public Video::SmackerDecoder {
@@ -57,59 +41,38 @@ public:
 	bool endOfFrames() const;
 };
 
-class AnimManager {
+class NightlongVideoDecoder {
 public:
-	AnimManager(TrecisionEngine *vm);
-	~AnimManager();
-
-private:
-	TrecisionEngine *_vm;
-
-	NightlongSmackerDecoder *_smkAnims[MAXSMACK];
-	uint16 _playingAnims[MAXSMACK];
-
-	FastFile _animFile[MAXSMACK]; // nlanim.cd1 / nlanim.cd2 / nlanim.cd3
-	int _curCD;
-	bool _bgAnimRestarted;
-
-	void openSmk(int slot, Common::SeekableReadStream *stream);
-	void openSmkAnim(int slot, const Common::String &name);
-	void toggleMuteBgAnim(uint16 animation);
-	void closeSmk(int slot);
-	void drawFrame(NightlongSmackerDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen);
-	void drawFrameSubtitles(Graphics::Surface *surface, int frameNum);
-	void setVideoRange(NightlongSmackerDecoder *smkDecoder, int &startFrame, int &endFrame);
-	void refreshSmkAnim(uint16 animation);
-	void handleEndOfVideo(int animation, int slot);
-	bool shouldShowAnim(int animation, Common::Rect curRect);
-
-	void drawSmkBackgroundFrame(int animation);
-	void drawSmkIconFrame(uint16 startIcon, uint16 iconNum);
-	void drawSmkActionFrame();
-	void swapCD(int cd);
-	void patchAnimTab();
-
-public:
-	Common::Rect _animRect;
-	SAnim _animTab[MAXANIM];
-
-	void smkGoto(int slot, int frame);
-	void smkToggleAudio(int slot, bool on);
-	void smkToggleTrackAudio(int slot, int track, bool on);
-	int16 smkCurFrame(int slot);
-	void smkStop(uint16 slot);
-	void refreshActionAnimation() { refreshSmkAnim(_playingAnims[kSmackerAction]); }
-	bool isActionActive() const { return _playingAnims[kSmackerAction] != 0; }
-	void playMovie(const Common::String &filename, int startFrame = 0, int endFrame = -1, bool singleChoice = false);
-	void startFullMotion();
-	void stopFullMotion();
+	NightlongVideoDecoder(bool isAmiga);
+	~NightlongVideoDecoder();
+	bool loadStream(Common::SeekableReadStream *stream);
+	void muteTrack(uint track, bool mute);
+	void setMute(bool mute);
+	bool forceSeekToFrame(uint frame);
+	bool endOfFrames() const;
 
-	void refreshAnim(int box);
-	void startSmkAnim(uint16 animation);
-	void stopAllSmkAnims();
+	// VideoDecoder functions
+	int getCurFrame() const;
+	uint16 getWidth() const;
+	uint16 getHeight() const;
+	const Graphics::Surface *decodeNextFrame();
+	uint32 getFrameCount() const;
+	const byte *getPalette();
+	void start();
+	void rewind();
+	bool needsUpdate() const;
+	void setEndFrame(uint frame);
+	bool endOfVideo() const;
+
+	bool loadFile(const Common::Path &filename);
+	const Common::Rect *getNextDirtyRect();
 
-	void syncGameStream(Common::Serializer &ser);
-	void loadAnimTab(Common::SeekableReadStreamEndian *stream);
+private:
+	bool _isAmiga;
+	NightlongSmackerDecoder *_smkDecoder;
+	Audio::SoundHandle _amigaSoundHandle;
+	Audio::Mixer *_mixer;
 };
+
 } // End of namespace Trecision
 #endif




More information about the Scummvm-git-logs mailing list