From 0152b7c47f0eef04bd01ae6e1cb808d25ccdd9a0 Mon Sep 17 00:00:00 2001
From: Filippos Karapetis
Date: Sat, 24 Sep 2016 11:49:54 +0300
Subject: CHEWY: Add initial video (CFO) player

The game's videos are modified FLICs. There are some changes needed
to our FLIC decoder, which are included in a separate commit
---
 engines/chewy/chewy.cpp             |   1 +
 engines/chewy/console.cpp           |   9 +-
 engines/chewy/graphics.cpp          |  41 ++++++
 engines/chewy/graphics.h            |   1 +
 engines/chewy/module.mk             |   3 +-
 engines/chewy/resource.cpp          |   8 ++
 engines/chewy/resource.h            |   7 +
 engines/chewy/video/cfo_decoder.cpp | 261 ++++++++++++++++++++++++++++++++++++
 engines/chewy/video/cfo_decoder.h   |  59 ++++++++
 9 files changed, 386 insertions(+), 4 deletions(-)
 create mode 100644 engines/chewy/video/cfo_decoder.cpp
 create mode 100644 engines/chewy/video/cfo_decoder.h

(limited to 'engines/chewy')

diff --git a/engines/chewy/chewy.cpp b/engines/chewy/chewy.cpp
index b5271d5969..b2b8cd1b22 100644
--- a/engines/chewy/chewy.cpp
+++ b/engines/chewy/chewy.cpp
@@ -72,6 +72,7 @@ Common::Error ChewyEngine::run() {
 
 	initialize();
 
+	//_graphics->playVideo(0);
 	_graphics->drawImage("episode1.tgp", 0);
 	//_sound->playSpeech(1);
 	//_sound->playSound(1);
diff --git a/engines/chewy/console.cpp b/engines/chewy/console.cpp
index bf7e49c2d0..deaa1e3a62 100644
--- a/engines/chewy/console.cpp
+++ b/engines/chewy/console.cpp
@@ -157,11 +157,14 @@ bool Console::Cmd_PlayVideo(int argc, const char **argv) {
 		return true;
 	}
 
+	detach();	// close the console
+	
 	int resNum = atoi(argv[1]);
-	debugPrintf("TODO: Play video %d", resNum);
-	// TODO
+	Graphics *g = new Graphics();
+	g->playVideo(resNum);
+	delete g;
 
-	return true;
+	return false;
 }
 
 bool Console::Cmd_VideoInfo(int argc, const char **argv) {
diff --git a/engines/chewy/graphics.cpp b/engines/chewy/graphics.cpp
index c3b29febff..887bb75bb4 100644
--- a/engines/chewy/graphics.cpp
+++ b/engines/chewy/graphics.cpp
@@ -21,10 +21,12 @@
  */
 
 #include "common/system.h"
+#include "common/events.h"
 #include "graphics/palette.h"
 
 #include "chewy/graphics.h"
 #include "chewy/resource.h"
+#include "chewy/video/cfo_decoder.h"
 
 namespace Chewy {
 
@@ -41,4 +43,43 @@ void Graphics::drawImage(Common::String filename, int imageNum) {
 	delete res;
 }
 
+void Graphics::playVideo(uint num) {
+	CfoDecoder *cfoDecoder = new CfoDecoder();
+	VideoResource *videoResource = new VideoResource("cut.tap");
+	Common::SeekableReadStream *videoStream = videoResource->getVideoStream(num);
+
+	if (!cfoDecoder->loadStream(videoStream)) {
+		delete videoResource;
+		delete cfoDecoder;
+		return;
+	}
+
+	uint16 x = (g_system->getWidth() - cfoDecoder->getWidth()) / 2;
+	uint16 y = (g_system->getHeight() - cfoDecoder->getHeight()) / 2;
+	bool skipVideo = false;
+
+	cfoDecoder->start();
+
+	while (!g_engine->shouldQuit() && !cfoDecoder->endOfVideo() && !skipVideo) {
+		if (cfoDecoder->needsUpdate()) {
+			const ::Graphics::Surface *frame = cfoDecoder->decodeNextFrame();
+			if (frame) {
+				g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, frame->w, frame->h);
+
+				if (cfoDecoder->hasDirtyPalette())
+					g_system->getPaletteManager()->setPalette(cfoDecoder->getPalette(), 0, 256);
+
+				g_system->updateScreen();
+			}
+		}
+
+		Common::Event event;
+		while (g_system->getEventManager()->pollEvent(event)) {
+			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+				skipVideo = true;
+		}
+
+		g_system->delayMillis(10);
+	}
+}
 } // End of namespace Chewy
diff --git a/engines/chewy/graphics.h b/engines/chewy/graphics.h
index 601b419171..c7d303d2a2 100644
--- a/engines/chewy/graphics.h
+++ b/engines/chewy/graphics.h
@@ -33,6 +33,7 @@ public:
 	~Graphics() {}
 
 	void drawImage(Common::String filename, int imageNum);
+	void playVideo(uint num);
 private:
 
 };
diff --git a/engines/chewy/module.mk b/engines/chewy/module.mk
index 5b96ab9ba3..2e2535f47a 100644
--- a/engines/chewy/module.mk
+++ b/engines/chewy/module.mk
@@ -6,7 +6,8 @@ MODULE_OBJS = \
 	detection.o \
 	graphics.o \
 	resource.o \
-	sound.o
+	sound.o \
+	video/cfo_decoder.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_CHEWY), DYNAMIC_PLUGIN)
diff --git a/engines/chewy/resource.cpp b/engines/chewy/resource.cpp
index 6a1b860d5b..a873edf563 100644
--- a/engines/chewy/resource.cpp
+++ b/engines/chewy/resource.cpp
@@ -22,6 +22,7 @@
 
 #include "common/debug.h"
 #include "common/stream.h"
+#include "common/substream.h"
 #include "common/textconsole.h"
 
 #include "chewy/chewy.h"
@@ -247,4 +248,11 @@ VideoChunk *VideoResource::getVideoHeader(uint num) {
 	return vid;
 }
 
+Common::SeekableReadStream *VideoResource::getVideoStream(uint num) {
+	assert(num < _chunkList.size());
+
+	Chunk *chunk = &_chunkList[num];
+	return new Common::SeekableSubReadStream(&_stream, chunk->pos, chunk->pos + chunk->size);
+}
+
 } // End of namespace Chewy
diff --git a/engines/chewy/resource.h b/engines/chewy/resource.h
index 238f0f2b60..d9665ceb4a 100644
--- a/engines/chewy/resource.h
+++ b/engines/chewy/resource.h
@@ -31,6 +31,7 @@
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 #include "common/random.h"
+#include "common/stream.h"
 
 namespace Chewy {
 
@@ -105,6 +106,11 @@ struct VideoChunk {
 	uint32 firstFrameOffset;
 };
 
+enum VideoFrameType {
+	kVideoFrameNormal = 0xF1FA,
+	kVideoFrameCustom = 0xFAF1
+};
+
 typedef Common::Array<Chunk> ChunkList;
 typedef Common::Array<TBFChunk> TBFChunkList;
 
@@ -157,6 +163,7 @@ public:
 	~VideoResource() {}
 
 	VideoChunk *getVideoHeader(uint num);
+	Common::SeekableReadStream *getVideoStream(uint num);
 };
 
 } // End of namespace Chewy
diff --git a/engines/chewy/video/cfo_decoder.cpp b/engines/chewy/video/cfo_decoder.cpp
new file mode 100644
index 0000000000..99fd665523
--- /dev/null
+++ b/engines/chewy/video/cfo_decoder.cpp
@@ -0,0 +1,261 @@
+/* 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/stream.h"
+#include "video/flic_decoder.h"
+
+#include "chewy/video/cfo_decoder.h"
+
+namespace Chewy {
+
+enum CustomSubChunk {
+	kChunkFadeIn = 0,
+	kChunkFadeOut = 1,
+	kChunkLoadMusic = 2,
+	kChunkLoadRaw = 3,
+	kChunkLoadVoc = 4,
+	kChunkPlayMusic = 5,
+	kChunkPlaySeq = 6,
+	kChunkPlayPattern = 7,
+	kChunkStopMusic = 8,
+	kChunkWaitMusicEnd = 9,
+	kChunkSetMusicVolume = 10,
+	kChunkSetLoopMode = 11,
+	kChunkPlayRaw = 12,
+	kChunkPlayVoc = 13,
+	kChunkSetSoundVolume = 14,
+	kChunkSetChannelVolume = 15,
+	kChunkFreeSoundEffect = 16,
+	kChunkMusicFadeIn = 17,
+	kChunkMusicFadeOut = 18,
+	kChunkSetStero = 19,
+	kChunkSetSpeed = 20,
+	kChunkClearScreen = 21
+};
+
+bool CfoDecoder::loadStream(Common::SeekableReadStream *stream) {
+	close();
+
+	if (stream->readUint32BE() != MKTAG('C', 'F', 'O', '\0'))
+		error("Corrupt video resource");
+
+	stream->readUint32LE();	// always 0
+
+	uint16 frameCount = stream->readUint16LE();
+	uint16 width = stream->readUint16LE();
+	uint16 height = stream->readUint16LE();
+
+	addTrack(new CfoVideoTrack(stream, frameCount, width, height));
+	return true;
+}
+
+CfoDecoder::CfoVideoTrack::CfoVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height) :
+	Video::FlicDecoder::FlicVideoTrack(stream, frameCount, width, height, true) {
+	readHeader();
+}
+
+void CfoDecoder::CfoVideoTrack::readHeader() {
+	_frameDelay = _startFrameDelay = _fileStream->readUint32LE();
+	_offsetFrame1 = _fileStream->readUint32LE();
+	_offsetFrame2 = 0;	// doesn't exist, as CFO videos aren't rewindable
+
+	_fileStream->seek(_offsetFrame1);
+}
+
+#define FRAME_TYPE 0xF1FA
+#define CUSTOM_FRAME_TYPE 0xFAF1
+
+const Graphics::Surface *CfoDecoder::CfoVideoTrack::decodeNextFrame() {
+	uint16 frameType;
+
+	// Read chunk
+	/*uint32 frameSize =*/ _fileStream->readUint32LE();
+	frameType = _fileStream->readUint16LE();
+
+	switch (frameType) {
+	case FRAME_TYPE:
+		handleFrame();
+		break;
+	case CUSTOM_FRAME_TYPE:
+		handleCustomFrame();
+		break;
+	default:
+		error("CfoDecoder::decodeFrame(): unknown main chunk type (type = 0x%02X)", frameType);
+		break;
+	}
+
+	_curFrame++;
+	_nextFrameStartTime += _frameDelay;
+
+	return _surface;
+}
+
+#define FLI_SETPAL 4
+#define FLI_SS2    7
+#define FLI_BRUN   15
+#define FLI_COPY   16
+#define PSTAMP     18
+
+void CfoDecoder::CfoVideoTrack::handleFrame() {
+	uint16 chunkCount = _fileStream->readUint16LE();
+
+	// Read subchunks
+	for (uint32 i = 0; i < chunkCount; ++i) {
+		uint32 frameSize = _fileStream->readUint32LE();
+		uint16 frameType = _fileStream->readUint16LE();
+		uint8 *data = new uint8[frameSize - 6];
+		_fileStream->read(data, frameSize - 6);
+
+		switch (frameType) {
+		case FLI_SETPAL:
+			unpackPalette(data);
+			_dirtyPalette = true;
+			break;
+		case FLI_SS2:
+			decodeDeltaFLC(data);
+			break;
+		case FLI_BRUN:
+			decodeByteRun(data);
+			break;
+		case FLI_COPY:
+			copyFrame(data);
+			break;
+		case PSTAMP:
+			/* PSTAMP - skip for now */
+			break;
+		default:
+			error("FlicDecoder::decodeNextFrame(): unknown subchunk type (type = 0x%02X)", frameType);
+			break;
+		}
+
+		delete[] data;
+	}
+}
+
+void CfoDecoder::CfoVideoTrack::handleCustomFrame() {
+	uint16 chunkCount = _fileStream->readUint16LE();
+
+	// Read subchunks
+	for (uint32 i = 0; i < chunkCount; ++i) {
+		uint32 frameSize = _fileStream->readUint32LE();
+		uint16 frameType = _fileStream->readUint16LE();
+		uint8 *data = new uint8[frameSize];
+		_fileStream->read(data, frameSize);
+
+		switch (frameType) {
+		case kChunkFadeIn:
+			warning("kChunkFadeIn");
+			// TODO
+			break;
+		case kChunkFadeOut:
+			warning("kChunkFadeOut");
+			// TODO
+			break;
+		case kChunkLoadMusic:
+			warning("kChunkLoadMusic");
+			// TODO
+			break;
+		case kChunkLoadRaw:
+			warning("kChunkLoadRaw");
+			// TODO
+			break;
+		case kChunkLoadVoc:
+			warning("kChunkLoadVoc");
+			// TODO
+			break;
+		case kChunkPlayMusic:
+			warning("kChunkPlayMusic");
+			break;
+		case kChunkPlaySeq:
+			warning("kChunkPlaySeq");
+			// TODO
+			break;
+		case kChunkPlayPattern:
+			warning("kChunkPlayPattern");
+			// TODO
+			break;
+		case kChunkStopMusic:
+			warning("kChunkStopMusic");
+			// TODO
+			break;
+		case kChunkWaitMusicEnd:
+			warning("kChunkWaitMusicEnd");
+			// TODO
+			break;
+		case kChunkSetMusicVolume:
+			warning("kChunkSetMusicVolume");
+			// TODO
+			break;
+		case kChunkSetLoopMode:
+			warning("kChunkSetLoopMode");
+			// TODO
+			break;
+		case kChunkPlayRaw:
+			warning("kChunkPlayRaw");
+			// TODO
+			break;
+		case kChunkPlayVoc:
+			warning("kChunkPlayVoc");
+			// TODO
+			break;
+		case kChunkSetSoundVolume:
+			warning("kChunkSetSoundVolume");
+			// TODO
+			break;
+		case kChunkSetChannelVolume:
+			warning("kChunkSetChannelVolume");
+			// TODO
+			break;
+		case kChunkFreeSoundEffect:
+			warning("kChunkFreeSoundEffect");
+			// TODO
+			break;
+		case kChunkMusicFadeIn:
+			warning("kChunkMusicFadeIn");
+			// TODO
+			break;
+		case kChunkMusicFadeOut:
+			warning("kChunkMusicFadeOut");
+			// TODO
+			break;
+		case kChunkSetStero:
+			warning("kChunkSetStero");
+			// TODO
+			break;
+		case kChunkSetSpeed:
+			warning("kChunkSetSpeed");
+			// TODO
+			break;
+		case kChunkClearScreen:
+			warning("kChunkClearScreen");
+			// TODO
+			break;
+		default:
+			error("Unknown subchunk: %d", frameType);
+			break;
+		}
+
+		delete[] data;
+	}
+}
+
+} // End of namespace Chewy
diff --git a/engines/chewy/video/cfo_decoder.h b/engines/chewy/video/cfo_decoder.h
new file mode 100644
index 0000000000..57c083858f
--- /dev/null
+++ b/engines/chewy/video/cfo_decoder.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ */
+
+#ifndef CHEWY_VIDEO_CFO_DECODER_H
+#define CHEWY_VIDEO_CFO_DECODER_H
+
+
+#include "graphics/surface.h"
+#include "video/flic_decoder.h"
+
+namespace Chewy {
+
+// A FLIC decoder, with a modified header and additional custom frames
+class CfoDecoder : public Video::FlicDecoder {
+public:
+	CfoDecoder() : Video::FlicDecoder() {}
+	virtual ~CfoDecoder() {}
+
+	bool loadStream(Common::SeekableReadStream *stream);
+
+protected:
+	class CfoVideoTrack : public Video::FlicDecoder::FlicVideoTrack {
+	public:
+		CfoVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height);
+		~CfoVideoTrack() {}
+
+		void readHeader();
+
+		bool isRewindable() const { return false; }
+		bool rewind() { return false; }
+
+		const ::Graphics::Surface *decodeNextFrame();
+		void handleFrame();
+		void handleCustomFrame();
+	};
+};
+
+} // End of namespace Chewy
+
+#endif
-- 
cgit v1.2.3