[Scummvm-cvs-logs] scummvm master -> 4637d55337d083d85bea762db7d59b47e92c0fbe

wjp wjp at usecode.org
Thu Feb 23 23:00:22 CET 2012


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

Summary:
307908662a VIDEO: Add a PlayStation stream decoder
df21e72fe2 SWORD1: Add support for PSX stream playback
8812f885bc SWORD2: Add support for PSX stream playback
ee35d32a36 VIDEO: Implement PSX stream v3 frame support
66cd8bdd68 VIDEO: Make PSX streams calculate frame timing solely from CD speed
8fea496890 VIDEO: Clarify which PSX streams we can play
a352c3cc00 SWORD1: Add support for the PSX demo videos
7a3e0ea453 SWORD1: Add some TODO's for PSX stream subtitles
4637d55337 Merge pull request #171 from clone2727/psx-stream-2


Commit: 307908662af4f171e442e3b6b209e72845f30c06
    https://github.com/scummvm/scummvm/commit/307908662af4f171e442e3b6b209e72845f30c06
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:16-08:00

Commit Message:
VIDEO: Add a PlayStation stream decoder

To be used for sword1/sword2 PSX video playback

Changed paths:
  A video/psx_decoder.cpp
  A video/psx_decoder.h
    video/module.mk



diff --git a/video/module.mk b/video/module.mk
index ceeac94..900a781 100644
--- a/video/module.mk
+++ b/video/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	coktel_decoder.o \
 	dxa_decoder.o \
 	flic_decoder.o \
+	psx_decoder.o \
 	qt_decoder.o \
 	smk_decoder.o \
 	video_decoder.o \
diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
new file mode 100644
index 0000000..4ed5a16
--- /dev/null
+++ b/video/psx_decoder.cpp
@@ -0,0 +1,638 @@
+/* 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.
+ *
+ */
+
+// PlayStation Stream demuxer and XA audio decoder based on FFmpeg/libav
+// MDEC video emulation based on http://kenai.com/downloads/jpsxdec/Old/PlayStation1_STR_format1-00.txt
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "audio/decoders/raw.h"
+#include "common/bitstream.h"
+#include "common/huffman.h"
+#include "common/memstream.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+#include "graphics/yuv_to_rgb.h"
+
+#include "video/psx_decoder.h"
+
+namespace Video {
+
+PSXStreamDecoder::PSXStreamDecoder(Common::Rational frameRate) {
+	assert(frameRate != 0);
+	initCommon();
+	_frameRate = frameRate;
+	_frameCount = 0;
+	_speed = kCDUnk;
+}
+
+PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) {
+	assert(speed != kCDUnk);
+	assert(frameCount != 0);
+	initCommon();
+	_frameCount = frameCount;
+	_speed = speed;
+	// frame rate will be calculated in loadStream()
+}
+
+PSXStreamDecoder::~PSXStreamDecoder() {
+	close();
+	delete _surface;
+	delete _huffman;
+}
+
+#define CODE_COUNT 113
+#define HUFFVAL(z, a) ((z << 8) | a)
+#define ESCAPE_CODE  ((uint32)-1) // arbitrary, just so we can tell what code it is
+#define END_OF_BLOCK ((uint32)-2) // arbitrary, just so we can tell what code it is
+#define GET_ZEROES(code) (code >> 8)
+#define GET_AC(code) ((int)(code & 0xff))
+
+static const uint32 s_huffmanCodes[CODE_COUNT] = {
+	// Regular codes
+	3, 3, 4, 5, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7,
+	32, 33, 34, 35, 36, 37, 38, 39, 8, 9, 10, 11,
+	12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+	23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17,
+	18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+	29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18,
+	19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+	30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+	25, 26, 27, 28, 29, 30, 31,
+
+	// Escape code
+	1,
+	// End of block code
+	2
+};
+
+static const byte s_huffmanLengths[CODE_COUNT] = {
+	// Regular codes
+	2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
+	8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10,
+	10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12,
+	12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13,
+	13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+	13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+	14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15,
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+	15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16,
+
+	// Escape code
+	6,
+	// End of block code
+	2
+};
+
+static const uint32 s_huffmanSymbols[CODE_COUNT] = {
+	// Regular codes
+	HUFFVAL(0, 1), HUFFVAL(1, 1), HUFFVAL(0, 2), HUFFVAL(2, 1), HUFFVAL(0, 3),
+	HUFFVAL(4, 1), HUFFVAL(3, 1), HUFFVAL(7, 1), HUFFVAL(6, 1), HUFFVAL(1, 2),
+	HUFFVAL(5, 1), HUFFVAL(2, 2), HUFFVAL(9, 1), HUFFVAL(0, 4), HUFFVAL(8, 1),
+	HUFFVAL(13, 1), HUFFVAL(0, 6), HUFFVAL(12, 1), HUFFVAL(11, 1), HUFFVAL(3, 2),
+	HUFFVAL(1, 3), HUFFVAL(0, 5), HUFFVAL(10, 1), HUFFVAL(16, 1), HUFFVAL(5, 2),
+	HUFFVAL(0, 7), HUFFVAL(2, 3), HUFFVAL(1, 4), HUFFVAL(15, 1), HUFFVAL(14, 1),
+	HUFFVAL(4, 2), HUFFVAL(0, 11), HUFFVAL(8, 2), HUFFVAL(4, 3), HUFFVAL(0, 10),
+	HUFFVAL(2, 4), HUFFVAL(7, 2), HUFFVAL(21, 1), HUFFVAL(20, 1), HUFFVAL(0, 9),
+	HUFFVAL(19, 1), HUFFVAL(18, 1), HUFFVAL(1, 5), HUFFVAL(3, 3), HUFFVAL(0, 8),
+	HUFFVAL(6, 2), HUFFVAL(17, 1), HUFFVAL(10, 2), HUFFVAL(9, 2), HUFFVAL(5, 3),
+	HUFFVAL(3, 4), HUFFVAL(2, 5), HUFFVAL(1, 7), HUFFVAL(1, 6), HUFFVAL(0, 15),
+	HUFFVAL(0, 14), HUFFVAL(0, 13), HUFFVAL(0, 12), HUFFVAL(26, 1), HUFFVAL(25, 1),
+	HUFFVAL(24, 1), HUFFVAL(23, 1), HUFFVAL(22, 1), HUFFVAL(0, 31), HUFFVAL(0, 30),
+	HUFFVAL(0, 29), HUFFVAL(0, 28), HUFFVAL(0, 27), HUFFVAL(0, 26), HUFFVAL(0, 25),
+	HUFFVAL(0, 24), HUFFVAL(0, 23), HUFFVAL(0, 22), HUFFVAL(0, 21), HUFFVAL(0, 20),
+	HUFFVAL(0, 19), HUFFVAL(0, 18), HUFFVAL(0, 17), HUFFVAL(0, 16), HUFFVAL(0, 40),
+	HUFFVAL(0, 39), HUFFVAL(0, 38), HUFFVAL(0, 37), HUFFVAL(0, 36), HUFFVAL(0, 35),
+	HUFFVAL(0, 34), HUFFVAL(0, 33), HUFFVAL(0, 32), HUFFVAL(1, 14), HUFFVAL(1, 13),
+	HUFFVAL(1, 12), HUFFVAL(1, 11), HUFFVAL(1, 10), HUFFVAL(1, 9), HUFFVAL(1, 8),
+	HUFFVAL(1, 18), HUFFVAL(1, 17), HUFFVAL(1, 16), HUFFVAL(1, 15), HUFFVAL(6, 3),
+	HUFFVAL(16, 2), HUFFVAL(15, 2), HUFFVAL(14, 2), HUFFVAL(13, 2), HUFFVAL(12, 2),
+	HUFFVAL(11, 2), HUFFVAL(31, 1), HUFFVAL(30, 1), HUFFVAL(29, 1), HUFFVAL(28, 1),
+	HUFFVAL(27, 1),
+
+	// Escape code
+	ESCAPE_CODE,
+	// End of block code
+	END_OF_BLOCK
+};
+
+void PSXStreamDecoder::initCommon() {
+	_stream = 0;
+	_audStream = 0;
+	_surface = new Graphics::Surface();
+	_yBuffer = _cbBuffer = _crBuffer = 0;
+	_huffman = new Common::Huffman(0, CODE_COUNT, s_huffmanCodes, s_huffmanLengths, s_huffmanSymbols);
+}
+
+#define RAW_CD_SECTOR_SIZE 2352
+
+#define CDXA_TYPE_MASK     0x0E
+#define CDXA_TYPE_DATA     0x08
+#define CDXA_TYPE_AUDIO    0x04
+#define CDXA_TYPE_VIDEO    0x02
+
+bool PSXStreamDecoder::loadStream(Common::SeekableReadStream *stream) {
+	close();
+
+	_stream = stream;
+
+	Common::SeekableReadStream *sector = readSector();
+
+	if (!sector) {
+		close();
+		return false;
+	}
+
+	// Rip out video info from the first frame
+	sector->seek(18);
+	byte sectorType = sector->readByte() & CDXA_TYPE_MASK;
+
+	if (sectorType != CDXA_TYPE_VIDEO && sectorType != CDXA_TYPE_DATA) {
+		close();
+		return false;
+	}
+
+	sector->seek(40);
+
+	uint16 width = sector->readUint16LE();
+	uint16 height = sector->readUint16LE();
+	_surface->create(width, height, g_system->getScreenFormat());
+
+	_macroBlocksW = (width + 15) / 16;
+	_macroBlocksH = (height + 15) / 16;
+	_yBuffer = new byte[_macroBlocksW * _macroBlocksH * 16 * 16];
+	_cbBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8];
+	_crBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8];
+
+	delete sector;
+	_stream->seek(0);
+
+	// Calculate frame rate based on CD speed
+	if (_speed != kCDUnk) {
+		// TODO: This algorithm is too basic and not accurate enough
+		// TODO: Count the number of sectors per frame to get a better estimate
+		_frameRate = Common::Rational(_speed * _frameCount, _stream->size() / RAW_CD_SECTOR_SIZE);
+		_frameRate.debugPrint(0, "Approximate PSX Stream Frame Rate:");
+	}
+
+	return true;
+}
+
+void PSXStreamDecoder::close() {
+	if (!_stream)
+		return;
+
+	delete _stream;
+	_stream = 0;
+
+	// Deinitialize sound
+	g_system->getMixer()->stopHandle(_audHandle);
+	_audStream = 0;
+
+	_surface->free();
+
+	memset(&_adpcmStatus, 0, sizeof(_adpcmStatus));
+
+	_macroBlocksW = _macroBlocksH = 0;
+	delete[] _yBuffer; _yBuffer = 0;
+	delete[] _cbBuffer; _cbBuffer = 0;
+	delete[] _crBuffer; _crBuffer = 0;
+
+	reset();
+}
+
+uint32 PSXStreamDecoder::getElapsedTime() const {
+	// TODO: Currently, the audio is always after the video so using this
+	// can often lead to gaps in the audio...
+	//if (_audStream)
+	//	return _mixer->getSoundElapsedTime(_audHandle);
+
+	return FixedRateVideoDecoder::getElapsedTime();
+}
+
+#define VIDEO_DATA_CHUNK_SIZE   2016
+#define VIDEO_DATA_HEADER_SIZE  56
+
+const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() {
+	Common::SeekableReadStream *sector = 0;
+	byte *partialFrame = 0;
+
+	while (!endOfVideo()) {
+		sector = readSector();
+
+		if (!sector)
+			error("Corrupt PSX stream sector");
+
+		sector->seek(0x11);
+		byte track = sector->readByte();
+		if (track >= 32)
+			error("Bad PSX stream track");
+
+		byte sectorType = sector->readByte() & CDXA_TYPE_MASK;
+
+		switch (sectorType) {
+		case CDXA_TYPE_DATA:
+		case CDXA_TYPE_VIDEO:
+			if (track == 1) {
+				sector->seek(28);
+				uint16 curSector = sector->readUint16LE();
+				uint16 sectorCount = sector->readUint16LE();
+				sector->readUint32LE();
+				uint16 frameSize = sector->readUint32LE();
+
+				if (curSector >= sectorCount)
+					error("Bad sector");
+
+				if (!partialFrame)
+					partialFrame = (byte *)malloc(sectorCount * VIDEO_DATA_CHUNK_SIZE);
+
+				sector->seek(VIDEO_DATA_HEADER_SIZE);
+				sector->read(partialFrame + curSector * VIDEO_DATA_CHUNK_SIZE, VIDEO_DATA_CHUNK_SIZE);
+
+				if (curSector == sectorCount - 1) {
+					// Done assembling the frame
+					Common::SeekableReadStream *frame = new Common::MemoryReadStream(partialFrame, frameSize, DisposeAfterUse::YES);
+
+					decodeFrame(frame);
+					
+					delete frame;
+					delete sector;
+
+					_curFrame++;
+					if (_curFrame == 0)
+						_startTime = g_system->getMillis();
+
+					return _surface;
+				}
+			} else
+				error("Unhandled multi-track video");
+			break;
+		case CDXA_TYPE_AUDIO:
+			// We only handle one audio channel so far
+			if (track == 1)
+				queueAudioFromSector(sector);
+			else
+				warning("Unhandled multi-track audio");
+			break;
+		default:
+			// This shows up way too often, but the other sectors
+			// are safe to ignore
+			//warning("Unknown PSX sector type 0x%x", sectorType);
+			break;
+		}
+
+		delete sector;
+	}
+
+	return 0;
+}
+
+static const byte s_syncHeader[12] = { 0x00, 0xff ,0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+Common::SeekableReadStream *PSXStreamDecoder::readSector() {
+	assert(_stream);
+
+	Common::SeekableReadStream *stream = _stream->readStream(RAW_CD_SECTOR_SIZE);
+
+	byte syncHeader[12];
+	stream->read(syncHeader, 12);
+	if (!memcmp(s_syncHeader, syncHeader, 12))
+		return stream;
+
+	return 0;
+}
+
+// Ha! It's palindromic!
+#define AUDIO_DATA_CHUNK_SIZE   2304
+#define AUDIO_DATA_SAMPLE_COUNT 4032 
+
+static const int s_xaTable[5][2] = {
+   {   0,   0 },
+   {  60,   0 },
+   { 115, -52 },
+   {  98, -55 },
+   { 122, -60 }
+};
+
+void PSXStreamDecoder::queueAudioFromSector(Common::SeekableReadStream *sector) {
+	assert(sector);
+
+	if (!_audStream) {
+		// Initialize audio stream
+		sector->seek(19);
+		byte format = sector->readByte();
+
+		bool stereo = (format & (1 << 0)) != 0;
+		uint rate = (format & (1 << 2)) ? 18900 : 37800;
+
+		_audStream = Audio::makeQueuingAudioStream(rate, stereo);
+		g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream);
+	}
+
+	sector->seek(24);
+
+	// This XA audio is different (yet similar) from normal XA audio! Watch out!
+	// TODO: It's probably similar enough to normal XA that we can merge it somehow...
+	// TODO: RTZ PSX needs the same audio code in a regular AudioStream class. Probably
+	// will do something similar to QuickTime and creating a base class 'ISOMode2Parser'
+	// or something similar.
+	byte *buf = new byte[AUDIO_DATA_CHUNK_SIZE];
+	sector->read(buf, AUDIO_DATA_CHUNK_SIZE);
+
+	int channels = _audStream->isStereo() ? 2 : 1;
+	int16 *dst = new int16[AUDIO_DATA_SAMPLE_COUNT];
+	int16 *leftChannel = dst;
+	int16 *rightChannel = dst + 1;
+
+	for (byte *src = buf; src < buf + AUDIO_DATA_CHUNK_SIZE; src += 128) {
+		for (int i = 0; i < 4; i++) {
+			int shift = 12 - (src[4 + i * 2] & 0xf);
+			int filter = src[4 + i * 2] >> 4;
+			int f0 = s_xaTable[filter][0];
+			int f1 = s_xaTable[filter][1];
+			int16 s_1 = _adpcmStatus[0].sample[0];
+			int16 s_2 = _adpcmStatus[0].sample[1];
+
+			for (int j = 0; j < 28; j++) {
+				byte d = src[16 + i + j * 4];
+				int t = (int8)(d << 4) >> 4;
+				int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
+				s_2 = s_1;
+				s_1 = CLIP<int>(s, -32768, 32767);
+				*leftChannel = s_1;
+				leftChannel += channels;
+			}
+
+			if (channels == 2) {
+				_adpcmStatus[0].sample[0] = s_1;
+				_adpcmStatus[0].sample[1] = s_2;
+				s_1 = _adpcmStatus[1].sample[0];
+				s_2 = _adpcmStatus[1].sample[1];
+			}
+
+			shift = 12 - (src[5 + i * 2] & 0xf);
+			filter = src[5 + i * 2] >> 4;
+			f0 = s_xaTable[filter][0];
+			f1 = s_xaTable[filter][1];
+
+			for (int j = 0; j < 28; j++) {
+				byte d = src[16 + i + j * 4];
+				int t = (int8)d >> 4;
+				int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
+				s_2 = s_1;
+				s_1 = CLIP<int>(s, -32768, 32767);
+
+				if (channels == 2) {
+					*rightChannel = s_1;
+					rightChannel += 2;
+				} else {
+					*leftChannel++ = s_1;
+				}
+			}
+
+			if (channels == 2) {
+				_adpcmStatus[1].sample[0] = s_1;
+				_adpcmStatus[1].sample[1] = s_2;
+			} else {
+				_adpcmStatus[0].sample[0] = s_1;
+				_adpcmStatus[0].sample[1] = s_2;
+			}
+		}
+	}
+
+	int flags = Audio::FLAG_16BITS;
+
+	if (_audStream->isStereo())
+		flags |= Audio::FLAG_STEREO;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+	flags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+
+	_audStream->queueBuffer((byte *)dst, AUDIO_DATA_SAMPLE_COUNT * 2, DisposeAfterUse::YES, flags);
+	delete[] buf;
+}
+
+void PSXStreamDecoder::decodeFrame(Common::SeekableReadStream *frame) {
+	// A frame is essentially an MPEG-1 intra frame
+
+	Common::BitStream16LEMSB bits(frame);
+
+	bits.skip(16); // unknown
+	bits.skip(16); // 0x3800
+	uint16 scale = bits.getBits(16);
+	uint16 version = bits.getBits(16);
+
+	if (version != 2 && version != 3)
+		error("Unknown PSX stream frame version");
+
+	for (int mbX = 0; mbX < _macroBlocksW; mbX++)
+		for (int mbY = 0; mbY < _macroBlocksH; mbY++)
+			decodeMacroBlock(&bits, mbX, mbY, scale, version);
+
+	// Output data onto the frame
+	Graphics::convertYUV420ToRGB(_surface, _yBuffer, _cbBuffer, _crBuffer, _surface->w, _surface->h, _macroBlocksW * 16, _macroBlocksW * 8);
+}
+
+void PSXStreamDecoder::decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version) {
+	int pitchY = _macroBlocksW * 16;
+	int pitchC = _macroBlocksW * 8;
+
+	// Note the strange order of red before blue
+	decodeBlock(bits, _crBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version);
+	decodeBlock(bits, _cbBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16, pitchY, scale, version);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8, pitchY, scale, version);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY, pitchY, scale, version);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY + 8, pitchY, scale, version);
+}
+
+// Standard JPEG/MPEG zig zag table
+static const byte s_zigZagTable[8 * 8] = {
+	 0,  1,  5,  6, 14, 15, 27, 28,
+	 2,  4,  7, 13, 16, 26, 29, 42,
+	 3,  8, 12, 17, 25, 30, 41, 43,
+	 9, 11, 18, 24, 31, 40, 44, 53,
+	10, 19, 23, 32, 39, 45, 52, 54,
+	20, 22, 33, 38, 46, 51, 55, 60,
+	21, 34, 37, 47, 50, 56, 59, 61,
+	35, 36, 48, 49, 57, 58, 62, 63
+};
+
+// One byte different from the standard MPEG-1 table
+static const byte s_quantizationTable[8 * 8] = {
+	 2, 16, 19, 22, 26, 27, 29, 34,
+	16, 16, 22, 24, 27, 29, 34, 37,
+	19, 22, 26, 27, 29, 34, 34, 38,
+	22, 22, 26, 27, 29, 34, 37, 40,
+	22, 26, 27, 29, 32, 35, 40, 48,
+	26, 27, 29, 32, 35, 40, 48, 58,
+	26, 27, 29, 34, 38, 46, 56, 69,
+	27, 29, 35, 38, 46, 56, 69, 83
+};
+
+void PSXStreamDecoder::dequantizeBlock(int *coefficients, float *block, uint16 scale) {
+	// Dequantize the data, un-zig-zagging as we go along
+	for (int i = 0; i < 8 * 8; i++) {
+		if (i == 0) // Special case for the DC coefficient
+			block[i] = coefficients[i] * s_quantizationTable[i];
+		else
+			block[i] = (float)coefficients[s_zigZagTable[i]] * s_quantizationTable[i] * scale / 8;
+	}
+}
+
+#define BLOCK_OVERFLOW_CHECK() \
+	if (count > 63) \
+		error("PSXStreamDecoder::readAC(): Too many coefficients")
+
+void PSXStreamDecoder::readAC(Common::BitStream *bits, int *block) {
+	// Clear the block first
+	for (int i = 0; i < 63; i++)
+		block[i] = 0;
+
+	int count = 0;
+
+	while (!bits->eos()) {
+		uint32 symbol = _huffman->getSymbol(*bits);
+
+		if (symbol == ESCAPE_CODE) {
+			// The escape code!
+			int zeroes = bits->getBits(6);
+			count += zeroes + 1;
+			BLOCK_OVERFLOW_CHECK();
+			block += zeroes;
+			*block++ = readSignedCoefficient(bits);
+		} else if (symbol == END_OF_BLOCK) {
+			// We're done
+			break;
+		} else {
+			// Normal huffman code
+			int zeroes = GET_ZEROES(symbol);
+			count += zeroes + 1;
+			BLOCK_OVERFLOW_CHECK();
+			block += zeroes;
+
+			if (bits->getBit())
+				*block++ = -GET_AC(symbol);
+			else
+				*block++ = GET_AC(symbol);
+		}
+	}
+}
+
+int PSXStreamDecoder::readSignedCoefficient(Common::BitStream *bits) {
+	uint val = bits->getBits(10);
+
+	// extend the sign
+	uint shift = 8 * sizeof(int) - 10;
+	return (int)(val << shift) >> shift;
+}
+
+// IDCT table built with :
+// _idct8x8[x][y] = cos(((2 * x + 1) * y) * (M_PI / 16.0)) * 0.5;
+// _idct8x8[x][y] /= sqrt(2.0) if y == 0
+static const double s_idct8x8[8][8] = {
+	{ 0.353553390593274,  0.490392640201615,  0.461939766255643,  0.415734806151273,  0.353553390593274,  0.277785116509801,  0.191341716182545,  0.097545161008064 },
+	{ 0.353553390593274,  0.415734806151273,  0.191341716182545, -0.097545161008064, -0.353553390593274, -0.490392640201615, -0.461939766255643, -0.277785116509801 },
+	{ 0.353553390593274,  0.277785116509801, -0.191341716182545, -0.490392640201615, -0.353553390593274,  0.097545161008064,  0.461939766255643,  0.415734806151273 },
+	{ 0.353553390593274,  0.097545161008064, -0.461939766255643, -0.277785116509801,  0.353553390593274,  0.415734806151273, -0.191341716182545, -0.490392640201615 },
+	{ 0.353553390593274, -0.097545161008064, -0.461939766255643,  0.277785116509801,  0.353553390593274, -0.415734806151273, -0.191341716182545,  0.490392640201615 },
+	{ 0.353553390593274, -0.277785116509801, -0.191341716182545,  0.490392640201615, -0.353553390593273, -0.097545161008064,  0.461939766255643, -0.415734806151273 },
+	{ 0.353553390593274, -0.415734806151273,  0.191341716182545,  0.097545161008064, -0.353553390593274,  0.490392640201615, -0.461939766255643,  0.277785116509801 },
+	{ 0.353553390593274, -0.490392640201615,  0.461939766255643, -0.415734806151273,  0.353553390593273, -0.277785116509801,  0.191341716182545, -0.097545161008064 }
+};
+
+void PSXStreamDecoder::idct(float *dequantData, float *result) {
+	// IDCT code based on JPEG's IDCT code
+	// TODO: Switch to the integer-based one mentioned in the docs
+	// This is by far the costliest operation here
+
+	float tmp[8 * 8];
+
+	// Apply 1D IDCT to rows
+	for (int y = 0; y < 8; y++) {
+		for (int x = 0; x < 8; x++) {
+			tmp[y + x * 8] = dequantData[0] * s_idct8x8[x][0]
+							+ dequantData[1] * s_idct8x8[x][1]
+							+ dequantData[2] * s_idct8x8[x][2]
+							+ dequantData[3] * s_idct8x8[x][3]
+							+ dequantData[4] * s_idct8x8[x][4]
+							+ dequantData[5] * s_idct8x8[x][5]
+							+ dequantData[6] * s_idct8x8[x][6]
+							+ dequantData[7] * s_idct8x8[x][7];
+		}
+
+		dequantData += 8;
+	}
+
+	// Apply 1D IDCT to columns
+	for (int x = 0; x < 8; x++) {
+		const float *u = tmp + x * 8;
+		for (int y = 0; y < 8; y++) {
+			result[y * 8 + x] = u[0] * s_idct8x8[y][0]
+								+ u[1] * s_idct8x8[y][1]
+								+ u[2] * s_idct8x8[y][2]
+								+ u[3] * s_idct8x8[y][3]
+								+ u[4] * s_idct8x8[y][4]
+								+ u[5] * s_idct8x8[y][5]
+								+ u[6] * s_idct8x8[y][6]
+								+ u[7] * s_idct8x8[y][7];
+		}
+	}
+}
+
+void PSXStreamDecoder::decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version) {
+	int dc;
+
+	if (version == 2) {
+		dc = readSignedCoefficient(bits);
+	} else {
+		// TODO
+		error("Unhandled PSX stream version 3 DC");
+	}
+
+	int coefficients[8 * 8];
+	coefficients[0] = dc; // Start us off with the DC
+	readAC(bits, &coefficients[1]); // Read in the AC
+
+	// Dequantize
+	float dequantData[8 * 8];
+	dequantizeBlock(coefficients, dequantData, scale);
+
+	// Perform IDCT
+	float idctData[8 * 8];
+	idct(dequantData, idctData);
+
+	// Now output the data
+	for (int y = 0; y < 8; y++) {
+		byte *start = block + pitch * y;
+
+		// Convert the result to be in the range [0, 255]
+		for (int x = 0; x < 8; x++)
+			*start++ = (int)CLIP<float>(idctData[y * 8 + x], -128.0f, 127.0f) + 128;
+	}
+}
+
+} // End of namespace Video
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
new file mode 100644
index 0000000..e8de507
--- /dev/null
+++ b/video/psx_decoder.h
@@ -0,0 +1,120 @@
+/* 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 VIDEO_PSX_DECODER_H
+#define VIDEO_PSX_DECODER_H
+
+#include "common/endian.h"
+#include "common/rational.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+namespace Audio {
+class QueuingAudioStream;
+}
+
+namespace Common {
+class BitStream;
+class Huffman;
+class SeekableReadStream;
+}
+
+namespace Graphics {
+struct PixelFormat;
+}
+
+namespace Video {
+
+/**
+ * Decoder for PSX stream videos.
+ *
+ * Video decoder used in engines:
+ *  - sword1 (psx)
+ *  - sword2 (psx)
+ */
+class PSXStreamDecoder : public FixedRateVideoDecoder {
+public:
+	// CD speed in sectors/second
+	// Calling code should use these enum values instead of the constants
+	enum CDSpeed {
+		kCDUnk = 0,
+		kCD1x = 75,
+		kCD2x = 150
+	};
+
+	PSXStreamDecoder(Common::Rational frameRate);
+	PSXStreamDecoder(CDSpeed speed, uint32 frameCount);
+	virtual ~PSXStreamDecoder();
+
+	bool loadStream(Common::SeekableReadStream *stream);
+	void close();
+
+	bool isVideoLoaded() const { return _stream != 0; }
+	uint16 getWidth() const { return _surface->w; }
+	uint16 getHeight() const { return _surface->h; }
+	uint32 getFrameCount() const { return _frameCount; }
+	uint32 getElapsedTime() const;
+	const Graphics::Surface *decodeNextFrame();
+	Graphics::PixelFormat getPixelFormat() const { return _surface->format; }
+	bool endOfVideo() const { return _stream->pos() >= _stream->size(); }
+
+protected:
+	// Hardcoded frame rate
+	Common::Rational getFrameRate() const { return _frameRate; }
+
+private:
+	void initCommon();
+	Common::SeekableReadStream *_stream;
+	Graphics::Surface *_surface;
+
+	CDSpeed _speed;
+	uint32 _frameCount;
+	Common::Rational _frameRate;
+
+	Audio::SoundHandle _audHandle;
+	Audio::QueuingAudioStream *_audStream;
+	void queueAudioFromSector(Common::SeekableReadStream *sector);
+
+	Common::Huffman *_huffman;
+	uint16 _macroBlocksW, _macroBlocksH;
+	byte *_yBuffer, *_cbBuffer, *_crBuffer;
+	void decodeFrame(Common::SeekableReadStream *frame);
+	void decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version);
+	void decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version);
+
+	void dequantizeBlock(int *coefficients, float *block, uint16 scale);
+	void readAC(Common::BitStream *bits, int *block);
+	void idct(float *dequantData, float *result);
+	int readSignedCoefficient(Common::BitStream *bits);
+
+	struct ADPCMStatus {
+		int16 sample[2];
+	} _adpcmStatus[2];
+
+	Common::SeekableReadStream *readSector();
+};
+
+} // End of namespace Video
+
+#endif


Commit: df21e72fe22132a802856bccc5f4bba27e4d3c17
    https://github.com/scummvm/scummvm/commit/df21e72fe22132a802856bccc5f4bba27e4d3c17
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:17-08:00

Commit Message:
SWORD1: Add support for PSX stream playback

Changed paths:
    engines/sword1/animation.cpp
    engines/sword1/animation.h
    engines/sword1/sword1.cpp



diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index d55a082..9e190e0 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -37,6 +37,11 @@
 
 #include "gui/message.h"
 
+#include "video/psx_decoder.h"
+#include "video/smk_decoder.h"
+
+#include "engines/util.h"
+
 namespace Sword1 {
 
 static const char *const sequenceList[20] = {
@@ -62,6 +67,31 @@ static const char *const sequenceList[20] = {
 	"credits",  // 19 CD2   credits, to follow "finale" sequence
 };
 
+// This is the list of the names of the PlayStation videos
+// TODO: fight.str, flashy.str, 
+static const char *const sequenceListPSX[20] = {
+	"e_ferr1",
+	"ladder1",
+	"steps1",
+	"sewer1",
+	"e_intro1",
+	"river1",
+	"truck1",
+	"grave1",
+	"montfcn1",
+	"tapesty1",
+	"ireland1",
+	"e_fin1",
+	"e_hist1",
+	"spanish1",
+	"well1",
+	"candle1",
+	"geodrop1",
+	"vulture1",
+	"",
+	"" // no credits?
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Basic movie player
 ///////////////////////////////////////////////////////////////////////////////
@@ -72,8 +102,8 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::
 	_decoderType = decoderType;
 	_decoder = decoder;
 
-	_white = 255;
-	_black = 0;
+	_white = (decoderType == kVideoDecoderPSX) ? _system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff) : 255;
+	_black = (decoderType == kVideoDecoderPSX) ? _system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : 0;
 }
 
 MoviePlayer::~MoviePlayer() {
@@ -142,6 +172,21 @@ bool MoviePlayer::load(uint32 id) {
 	case kVideoDecoderSMK:
 		filename = Common::String::format("%s.smk", sequenceList[id]);
 		break;
+	case kVideoDecoderPSX:
+		filename = Common::String::format("%s.str", sequenceListPSX[id]);
+
+		// Need to switch to true color
+		initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0);
+
+		// Need to load here in case it fails in which case we'd need
+		// to go back to paletted mode
+		if (_decoder->loadFile(filename)) {
+			return true;
+		} else {
+			initGraphics(g_system->getWidth(), g_system->getHeight(), true);
+			return false;
+		}
+		break;
 	}
 
 	return _decoder->loadFile(filename.c_str());
@@ -179,6 +224,10 @@ void MoviePlayer::play() {
 }
 
 void MoviePlayer::performPostProcessing(byte *screen) {
+	// TODO
+	if (_decoderType == kVideoDecoderPSX)
+		return;
+
 	if (!_movieTexts.empty()) {
 		if (_decoder->getCurFrame() == _movieTexts.front()._startFrame) {
 			_textMan->makeTextSprite(2, (const uint8 *)_movieTexts.front()._text.c_str(), 600, LETTER_COL);
@@ -206,10 +255,10 @@ void MoviePlayer::performPostProcessing(byte *screen) {
 			for (x = 0; x < _textWidth; x++) {
 				switch (src[x]) {
 				case BORDER_COL:
-					dst[x] = findBlackPalIndex();
+					dst[x] = getBlackColor();
 					break;
 				case LETTER_COL:
-					dst[x] = findWhitePalIndex();
+					dst[x] = getWhiteColor();
 					break;
 				}
 			}
@@ -229,12 +278,12 @@ void MoviePlayer::performPostProcessing(byte *screen) {
 
 		for (y = 0; y < _textHeight; y++) {
 			if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
-				memset(dst + _textX, findBlackPalIndex(), _textWidth);
+				memset(dst + _textX, getBlackColor(), _textWidth);
 			} else {
 				if (frameX > _textX)
-					memset(dst + _textX, findBlackPalIndex(), frameX - _textX);
+					memset(dst + _textX, getBlackColor(), frameX - _textX);
 				if (frameX + frameWidth < _textX + _textWidth)
-					memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + _textWidth - (frameX + frameWidth));
+					memset(dst + frameX + frameWidth, getBlackColor(), _textX + _textWidth - (frameX + frameWidth));
 			}
 
 			dst += _system->getWidth();
@@ -246,14 +295,19 @@ void MoviePlayer::performPostProcessing(byte *screen) {
 }
 
 bool MoviePlayer::playVideo() {
+	bool skipped = false;
 	uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2;
 	uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2;
 
-	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
+	while (!_vm->shouldQuit() && !_decoder->endOfVideo() && !skipped) {
 		if (_decoder->needsUpdate()) {
 			const Graphics::Surface *frame = _decoder->decodeNextFrame();
-			if (frame)
-				_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+			if (frame) {
+				if (_decoderType == kVideoDecoderPSX)
+					drawFramePSX(frame);
+				else
+					_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+			}
 
 			if (_decoder->hasDirtyPalette()) {
 				_decoder->setSystemPalette();
@@ -293,22 +347,44 @@ bool MoviePlayer::playVideo() {
 		Common::Event event;
 		while (_vm->_system->getEventManager()->pollEvent(event))
 			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
-				return false;
+				skipped = true;
 
 		_vm->_system->delayMillis(10);
 	}
 
-	return !_vm->shouldQuit();
+	if (_decoderType == kVideoDecoderPSX) {
+		// Need to jump back to paletted color
+		initGraphics(g_system->getWidth(), g_system->getHeight(), true);
+	}
+
+	return !_vm->shouldQuit() && !skipped;
 }
 
-byte MoviePlayer::findBlackPalIndex() {
+uint32 MoviePlayer::getBlackColor() {
 	return _black;
 }
 
-byte MoviePlayer::findWhitePalIndex() {
+uint32 MoviePlayer::getWhiteColor() {
 	return _white;
 }
 
+void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {
+	// The PSX videos have half resolution
+
+	Graphics::Surface scaledFrame;
+	scaledFrame.create(frame->w, frame->h * 2, frame->format);
+
+	for (int y = 0; y < scaledFrame.h; y++)
+		memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel);
+
+	uint16 x = (g_system->getWidth() - scaledFrame.w) / 2;
+	uint16 y = (g_system->getHeight() - scaledFrame.h) / 2;
+
+	_vm->_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
+
+	scaledFrame.free();
+}
+
 DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
 	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
 }
@@ -328,6 +404,23 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *
 	Common::String filename;
 	Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle;
 
+	// For the PSX version, we'll try the PlayStation stream files
+	if (vm->isPsx()) {
+		filename = Common::String(sequenceListPSX[id]) + ".str";
+
+		if (Common::File::exists(filename)) {
+#ifdef USE_RGB_COLOR
+			// All BS1 PSX videos seem to be 15fps
+			Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(15);
+			return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX);
+#else
+			GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK"));
+			dialog.runModal();
+			return 0;
+#endif
+		}
+	}
+
 	filename = Common::String::format("%s.smk", sequenceList[id]);
 
 	if (Common::File::exists(filename)) {
diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h
index 1c03c66..23dae7f 100644
--- a/engines/sword1/animation.h
+++ b/engines/sword1/animation.h
@@ -24,7 +24,6 @@
 #define SWORD1_ANIMATION_H
 
 #include "video/dxa_decoder.h"
-#include "video/smk_decoder.h"
 #include "video/video_decoder.h"
 
 #include "common/list.h"
@@ -38,7 +37,8 @@ namespace Sword1 {
 
 enum DecoderType {
 	kVideoDecoderDXA = 0,
-	kVideoDecoderSMK = 1
+	kVideoDecoderSMK = 1,
+	kVideoDecoderPSX = 2
 };
 
 class MovieText {
@@ -80,7 +80,7 @@ protected:
 	OSystem *_system;
 	Common::List<MovieText> _movieTexts;
 	int _textX, _textY, _textWidth, _textHeight;
-	byte _white, _black;
+	uint32 _white, _black;
 	DecoderType _decoderType;
 
 	Video::VideoDecoder *_decoder;
@@ -89,9 +89,10 @@ protected:
 
 	bool playVideo();
 	void performPostProcessing(byte *screen);
+	void drawFramePSX(const Graphics::Surface *frame);
 
-	byte findBlackPalIndex();
-	byte findWhitePalIndex();
+	uint32 getBlackColor();
+	uint32 getWhiteColor();
 };
 
 MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system);
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index 865e025..75e8f72 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -62,8 +62,9 @@ SwordEngine::SwordEngine(OSystem *syst)
 	SearchMan.addSubDirectoryMatching(gameDataDir, "speech");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "smackshi");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "english");//PSX Demo
-	SearchMan.addSubDirectoryMatching(gameDataDir, "italian");//PSX Demo
+	SearchMan.addSubDirectoryMatching(gameDataDir, "streams"); // PSX videos
+	SearchMan.addSubDirectoryMatching(gameDataDir, "english"); // PSX Demo
+	SearchMan.addSubDirectoryMatching(gameDataDir, "italian"); // PSX Demo
 
 	_console = new SwordConsole(this);
 }


Commit: 8812f885bc95f0e4af05c333780eb77a8c2eaa19
    https://github.com/scummvm/scummvm/commit/8812f885bc95f0e4af05c333780eb77a8c2eaa19
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:18-08:00

Commit Message:
SWORD2: Add support for PSX stream playback

Changed paths:
    engines/sword2/animation.cpp
    engines/sword2/animation.h
    engines/sword2/function.cpp
    engines/sword2/sword2.cpp



diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp
index 80b4809..e77ae98 100644
--- a/engines/sword2/animation.cpp
+++ b/engines/sword2/animation.cpp
@@ -40,6 +40,11 @@
 
 #include "gui/message.h"
 
+#include "video/smk_decoder.h"
+#include "video/psx_decoder.h"
+
+#include "engines/util.h"
+
 namespace Sword2 {
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -66,6 +71,10 @@ MoviePlayer::~MoviePlayer() {
  * @param id the id of the file
  */
 bool MoviePlayer::load(const char *name) {
+	// This happens when quitting during the "eye" cutscene.
+	if (_vm->shouldQuit())
+		return false;
+
 	if (_decoderType == kVideoDecoderDXA)
 		_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name);
 	else
@@ -81,16 +90,26 @@ bool MoviePlayer::load(const char *name) {
 	case kVideoDecoderSMK:
 		filename = Common::String::format("%s.smk", name);
 		break;
+	case kVideoDecoderPSX:
+		filename = Common::String::format("%s.str", name);
+
+		// Need to switch to true color
+		initGraphics(640, 480, true, 0);
+
+		// Need to load here in case it fails in which case we'd need
+		// to go back to paletted mode
+		if (_decoder->loadFile(filename)) {
+			return true;
+		} else {
+			initGraphics(640, 480, true);
+			return false;
+		}
 	}
 
 	return _decoder->loadFile(filename.c_str());
 }
 
 void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) {
-	// This happens when quitting during the "eye" cutscene.
-	if (_vm->shouldQuit())
-		return;
-
 	_leadOutFrame = _decoder->getFrameCount();
 	if (_leadOutFrame > 60)
 		_leadOutFrame -= 60;
@@ -120,6 +139,11 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI
 
 	while (_snd->isSoundHandleActive(*_bgSoundHandle))
 		_system->delayMillis(100);
+
+	if (_decoderType == kVideoDecoderPSX) {
+		// Need to jump back to paletted color
+		initGraphics(640, 480, true);
+	}
 }
 
 void MoviePlayer::openTextObject(uint32 index) {
@@ -165,7 +189,7 @@ void MoviePlayer::openTextObject(uint32 index) {
 	}
 }
 
-void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) {
+void MoviePlayer::closeTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) {
 	if (index < _numMovieTexts) {
 		MovieText *text = &_movieTexts[index];
 
@@ -180,23 +204,23 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) {
 
 				int frameWidth = _decoder->getWidth();
 				int frameHeight = _decoder->getHeight();
+
+				if (_decoderType == kVideoDecoderPSX)
+					frameHeight *= 2;
+
 				int frameX = (_system->getWidth() - frameWidth) / 2;
 				int frameY = (_system->getHeight() - frameHeight) / 2;
-				byte black = findBlackPalIndex();
-
-				byte *dst = screen + _textY * pitch;
+				uint32 black = getBlackColor();
 
 				for (int y = 0; y < text->_textSprite.h; y++) {
 					if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
-						memset(dst + _textX, black, text->_textSprite.w);
+						screen->hLine(_textX, _textY + y, _textX + text->_textSprite.w, black);
 					} else {
 						if (frameX > _textX)
-							memset(dst + _textX, black, frameX - _textX);
+							screen->hLine(_textX, _textY + y, frameX, black);
 						if (frameX + frameWidth < _textX + text->_textSprite.w)
-							memset(dst + frameX + frameWidth, black, _textX + text->_textSprite.w - (frameX + frameWidth));
+							screen->hLine(frameX + frameWidth, _textY + y, text->_textSprite.w, black);
 					}
-
-					dst += pitch;
 				}
 			}
 
@@ -206,11 +230,24 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) {
 	}
 }
 
-void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) {
+#define PUT_PIXEL(c) \
+	switch (screen->format.bytesPerPixel) { \
+	case 1: \
+		*dst = (c); \
+		break; \
+	case 2: \
+		WRITE_UINT16(dst, (c)); \
+		break; \
+	case 4: \
+		WRITE_UINT32(dst, (c)); \
+		break; \
+	}
+
+void MoviePlayer::drawTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) {
 	MovieText *text = &_movieTexts[index];
 
-	byte white = findWhitePalIndex();
-	byte black = findBlackPalIndex();
+	uint32 white = getWhiteColor();
+	uint32 black = getBlackColor();
 
 	if (text->_textMem && _textSurface) {
 		byte *src = text->_textSprite.data;
@@ -226,17 +263,20 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) {
 			src = psxSpriteBuffer;
 		}
 
-		byte *dst = screen + _textY * pitch + _textX;
-
 		for (int y = 0; y < height; y++) {
+			byte *dst = (byte *)screen->getBasePtr(_textX, _textY + y);
+
 			for (int x = 0; x < width; x++) {
-				if (src[x] == 1)
-					dst[x] = black;
-				else if (src[x] == 255)
-					dst[x] = white;
+				if (src[x] == 1) {
+					PUT_PIXEL(black);
+				} else if (src[x] == 255) {
+					PUT_PIXEL(white);
+				}
+
+				dst += screen->format.bytesPerPixel;
 			}
+
 			src += width;
-			dst += pitch;
 		}
 
 		// Free buffer used to resize psx sprite
@@ -245,7 +285,9 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) {
 	}
 }
 
-void MoviePlayer::performPostProcessing(byte *screen, uint16 pitch) {
+#undef PUT_PIXEL
+
+void MoviePlayer::performPostProcessing(Graphics::Surface *screen, uint16 pitch) {
 	MovieText *text;
 	int frame = _decoder->getCurFrame();
 
@@ -286,8 +328,12 @@ bool MoviePlayer::playVideo() {
 	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
 		if (_decoder->needsUpdate()) {
 			const Graphics::Surface *frame = _decoder->decodeNextFrame();
-			if (frame)
-				_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+			if (frame) {
+				if (_decoderType == kVideoDecoderPSX)
+					drawFramePSX(frame);
+				else
+					_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+			}
 
 			if (_decoder->hasDirtyPalette()) {
 				_decoder->setSystemPalette();
@@ -319,7 +365,7 @@ bool MoviePlayer::playVideo() {
 			}
 
 			Graphics::Surface *screen = _vm->_system->lockScreen();
-			performPostProcessing((byte *)screen->pixels, screen->pitch);
+			performPostProcessing(screen, screen->pitch);
 			_vm->_system->unlockScreen();
 			_vm->_system->updateScreen();
 		}
@@ -335,12 +381,29 @@ bool MoviePlayer::playVideo() {
 	return !_vm->shouldQuit();
 }
 
-byte MoviePlayer::findBlackPalIndex() {
-	return _black;
+uint32 MoviePlayer::getBlackColor() {
+	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black;
 }
 
-byte MoviePlayer::findWhitePalIndex() {
-	return _white;
+uint32 MoviePlayer::getWhiteColor() {
+	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF) : _white;
+}
+
+void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {
+	// The PSX videos have half resolution
+
+	Graphics::Surface scaledFrame;
+	scaledFrame.create(frame->w, frame->h * 2, frame->format);
+
+	for (int y = 0; y < scaledFrame.h; y++)
+		memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel);
+
+	uint16 x = (g_system->getWidth() - scaledFrame.w) / 2;
+	uint16 y = (g_system->getHeight() - scaledFrame.h) / 2;
+
+	_vm->_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
+
+	scaledFrame.free();
 }
 
 DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
@@ -358,10 +421,23 @@ uint32 DXADecoderWithSound::getElapsedTime() const {
 // Factory function for creating the appropriate cutscene player
 ///////////////////////////////////////////////////////////////////////////////
 
-MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system) {
+MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount) {
 	Common::String filename;
 	Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle;
 
+	filename = Common::String::format("%s.str", name);
+
+	if (Common::File::exists(filename)) {
+#ifdef USE_RGB_COLOR
+		Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount);
+		return new MoviePlayer(vm, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX);
+#else
+		GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK"));
+		dialog.runModal();
+		return NULL;
+#endif
+	}
+
 	filename = Common::String::format("%s.smk", name);
 
 	if (Common::File::exists(filename)) {
diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h
index 1f5fced..3ef8dac 100644
--- a/engines/sword2/animation.h
+++ b/engines/sword2/animation.h
@@ -26,7 +26,6 @@
 #define SWORD2_ANIMATION_H
 
 #include "video/dxa_decoder.h"
-#include "video/smk_decoder.h"
 #include "video/video_decoder.h"
 #include "audio/mixer.h"
 
@@ -36,7 +35,8 @@ namespace Sword2 {
 
 enum DecoderType {
 	kVideoDecoderDXA = 0,
-	kVideoDecoderSMK = 1
+	kVideoDecoderSMK = 1,
+	kVideoDecoderPSX = 2
 };
 
 struct MovieText {
@@ -93,18 +93,19 @@ protected:
 	uint32 _leadOut;
 	int _leadOutFrame;
 
-	void performPostProcessing(byte *screen, uint16 pitch);
+	void performPostProcessing(Graphics::Surface *screen, uint16 pitch);
 	bool playVideo();
+	void drawFramePSX(const Graphics::Surface *frame);
 
 	void openTextObject(uint32 index);
-	void closeTextObject(uint32 index, byte *screen, uint16 pitch);
-	void drawTextObject(uint32 index, byte *screen, uint16 pitch);
+	void closeTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch);
+	void drawTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch);
 
-	byte findBlackPalIndex();
-	byte findWhitePalIndex();
+	uint32 getBlackColor();
+	uint32 getWhiteColor();
 };
 
-MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system);
+MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount);
 
 } // End of namespace Sword2
 
diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp
index 60ee617..836b252 100644
--- a/engines/sword2/function.cpp
+++ b/engines/sword2/function.cpp
@@ -2137,7 +2137,9 @@ int32 Logic::fnPlaySequence(int32 *params) {
 	// pause sfx during sequence
 	_vm->_sound->pauseFx();
 
-	_moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system);
+	uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0;
+
+	_moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system, frameCount);
 
 	if (_moviePlayer && _moviePlayer->load(filename)) {
 		_moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut);
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 3b79652..bdfc388 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -255,6 +255,7 @@ Sword2Engine::Sword2Engine(OSystem *syst) : Engine(syst), _rnd("sword2") {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "sword2");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "smacks");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "streams"); // PSX video
 
 	if (!scumm_stricmp(ConfMan.get("gameid").c_str(), "sword2demo") || !scumm_stricmp(ConfMan.get("gameid").c_str(), "sword2psxdemo"))
 		_features = GF_DEMO;


Commit: ee35d32a362d58891fedff9843867397f2d4497b
    https://github.com/scummvm/scummvm/commit/ee35d32a362d58891fedff9843867397f2d4497b
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:18-08:00

Commit Message:
VIDEO: Implement PSX stream v3 frame support

Changed paths:
    video/psx_decoder.cpp
    video/psx_decoder.h



diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
index 4ed5a16..a0705e8 100644
--- a/video/psx_decoder.cpp
+++ b/video/psx_decoder.cpp
@@ -58,17 +58,53 @@ PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) {
 PSXStreamDecoder::~PSXStreamDecoder() {
 	close();
 	delete _surface;
-	delete _huffman;
+	delete _acHuffman;
+	delete _dcHuffmanLuma;
+	delete _dcHuffmanChroma;
 }
 
-#define CODE_COUNT 113
-#define HUFFVAL(z, a) ((z << 8) | a)
+// Here are the codes/lengths/symbols that are used for decoding
+// DC coefficients (version 3 frames only)
+
+#define DC_CODE_COUNT 9
+#define DC_HUFF_VAL(b, n, p) (((b) << 16) | ((n) << 8) | (p))
+#define GET_DC_BITS(x) ((x) >> 16)
+#define GET_DC_NEG(x) ((int)(((x) >> 8) & 0xff))
+#define GET_DC_POS(x) ((int)((x) & 0xff))
+
+static const uint32 s_huffmanDCChromaCodes[DC_CODE_COUNT] = {
+	254, 126, 62, 30, 14, 6, 2, 1, 0
+};
+
+static const byte s_huffmanDCChromaLengths[DC_CODE_COUNT] = {
+	8, 7, 6, 5, 4, 3, 2, 2, 2
+};
+
+static const uint32 s_huffmanDCLumaCodes[DC_CODE_COUNT] = {
+	126, 62, 30, 14, 6, 5, 1, 0, 4
+};
+
+static const byte s_huffmanDCLumaLengths[DC_CODE_COUNT] = {
+	7, 6, 5, 4, 3, 3, 2, 2, 3
+};
+
+static const uint32 s_huffmanDCSymbols[DC_CODE_COUNT] = {
+	DC_HUFF_VAL(8, 255, 128), DC_HUFF_VAL(7, 127, 64), DC_HUFF_VAL(6, 63, 32),
+	DC_HUFF_VAL(5, 31, 16), DC_HUFF_VAL(4, 15, 8), DC_HUFF_VAL(3, 7, 4),
+	DC_HUFF_VAL(2, 3, 2), DC_HUFF_VAL(1, 1, 1), DC_HUFF_VAL(0, 0, 0)
+};
+
+// Here are the codes/lengths/symbols that are used for decoding
+// DC coefficients (version 2 and 3 frames)
+
+#define AC_CODE_COUNT 113
+#define AC_HUFF_VAL(z, a) ((z << 8) | a)
 #define ESCAPE_CODE  ((uint32)-1) // arbitrary, just so we can tell what code it is
 #define END_OF_BLOCK ((uint32)-2) // arbitrary, just so we can tell what code it is
-#define GET_ZEROES(code) (code >> 8)
-#define GET_AC(code) ((int)(code & 0xff))
+#define GET_AC_ZERO_RUN(code) (code >> 8)
+#define GET_AC_COEFFICIENT(code) ((int)(code & 0xff))
 
-static const uint32 s_huffmanCodes[CODE_COUNT] = {
+static const uint32 s_huffmanACCodes[AC_CODE_COUNT] = {
 	// Regular codes
 	3, 3, 4, 5, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7,
 	32, 33, 34, 35, 36, 37, 38, 39, 8, 9, 10, 11,
@@ -87,7 +123,7 @@ static const uint32 s_huffmanCodes[CODE_COUNT] = {
 	2
 };
 
-static const byte s_huffmanLengths[CODE_COUNT] = {
+static const byte s_huffmanACLengths[AC_CODE_COUNT] = {
 	// Regular codes
 	2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
 	8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10,
@@ -106,31 +142,31 @@ static const byte s_huffmanLengths[CODE_COUNT] = {
 	2
 };
 
-static const uint32 s_huffmanSymbols[CODE_COUNT] = {
+static const uint32 s_huffmanACSymbols[AC_CODE_COUNT] = {
 	// Regular codes
-	HUFFVAL(0, 1), HUFFVAL(1, 1), HUFFVAL(0, 2), HUFFVAL(2, 1), HUFFVAL(0, 3),
-	HUFFVAL(4, 1), HUFFVAL(3, 1), HUFFVAL(7, 1), HUFFVAL(6, 1), HUFFVAL(1, 2),
-	HUFFVAL(5, 1), HUFFVAL(2, 2), HUFFVAL(9, 1), HUFFVAL(0, 4), HUFFVAL(8, 1),
-	HUFFVAL(13, 1), HUFFVAL(0, 6), HUFFVAL(12, 1), HUFFVAL(11, 1), HUFFVAL(3, 2),
-	HUFFVAL(1, 3), HUFFVAL(0, 5), HUFFVAL(10, 1), HUFFVAL(16, 1), HUFFVAL(5, 2),
-	HUFFVAL(0, 7), HUFFVAL(2, 3), HUFFVAL(1, 4), HUFFVAL(15, 1), HUFFVAL(14, 1),
-	HUFFVAL(4, 2), HUFFVAL(0, 11), HUFFVAL(8, 2), HUFFVAL(4, 3), HUFFVAL(0, 10),
-	HUFFVAL(2, 4), HUFFVAL(7, 2), HUFFVAL(21, 1), HUFFVAL(20, 1), HUFFVAL(0, 9),
-	HUFFVAL(19, 1), HUFFVAL(18, 1), HUFFVAL(1, 5), HUFFVAL(3, 3), HUFFVAL(0, 8),
-	HUFFVAL(6, 2), HUFFVAL(17, 1), HUFFVAL(10, 2), HUFFVAL(9, 2), HUFFVAL(5, 3),
-	HUFFVAL(3, 4), HUFFVAL(2, 5), HUFFVAL(1, 7), HUFFVAL(1, 6), HUFFVAL(0, 15),
-	HUFFVAL(0, 14), HUFFVAL(0, 13), HUFFVAL(0, 12), HUFFVAL(26, 1), HUFFVAL(25, 1),
-	HUFFVAL(24, 1), HUFFVAL(23, 1), HUFFVAL(22, 1), HUFFVAL(0, 31), HUFFVAL(0, 30),
-	HUFFVAL(0, 29), HUFFVAL(0, 28), HUFFVAL(0, 27), HUFFVAL(0, 26), HUFFVAL(0, 25),
-	HUFFVAL(0, 24), HUFFVAL(0, 23), HUFFVAL(0, 22), HUFFVAL(0, 21), HUFFVAL(0, 20),
-	HUFFVAL(0, 19), HUFFVAL(0, 18), HUFFVAL(0, 17), HUFFVAL(0, 16), HUFFVAL(0, 40),
-	HUFFVAL(0, 39), HUFFVAL(0, 38), HUFFVAL(0, 37), HUFFVAL(0, 36), HUFFVAL(0, 35),
-	HUFFVAL(0, 34), HUFFVAL(0, 33), HUFFVAL(0, 32), HUFFVAL(1, 14), HUFFVAL(1, 13),
-	HUFFVAL(1, 12), HUFFVAL(1, 11), HUFFVAL(1, 10), HUFFVAL(1, 9), HUFFVAL(1, 8),
-	HUFFVAL(1, 18), HUFFVAL(1, 17), HUFFVAL(1, 16), HUFFVAL(1, 15), HUFFVAL(6, 3),
-	HUFFVAL(16, 2), HUFFVAL(15, 2), HUFFVAL(14, 2), HUFFVAL(13, 2), HUFFVAL(12, 2),
-	HUFFVAL(11, 2), HUFFVAL(31, 1), HUFFVAL(30, 1), HUFFVAL(29, 1), HUFFVAL(28, 1),
-	HUFFVAL(27, 1),
+	AC_HUFF_VAL(0, 1), AC_HUFF_VAL(1, 1), AC_HUFF_VAL(0, 2), AC_HUFF_VAL(2, 1), AC_HUFF_VAL(0, 3),
+	AC_HUFF_VAL(4, 1), AC_HUFF_VAL(3, 1), AC_HUFF_VAL(7, 1), AC_HUFF_VAL(6, 1), AC_HUFF_VAL(1, 2),
+	AC_HUFF_VAL(5, 1), AC_HUFF_VAL(2, 2), AC_HUFF_VAL(9, 1), AC_HUFF_VAL(0, 4), AC_HUFF_VAL(8, 1),
+	AC_HUFF_VAL(13, 1), AC_HUFF_VAL(0, 6), AC_HUFF_VAL(12, 1), AC_HUFF_VAL(11, 1), AC_HUFF_VAL(3, 2),
+	AC_HUFF_VAL(1, 3), AC_HUFF_VAL(0, 5), AC_HUFF_VAL(10, 1), AC_HUFF_VAL(16, 1), AC_HUFF_VAL(5, 2),
+	AC_HUFF_VAL(0, 7), AC_HUFF_VAL(2, 3), AC_HUFF_VAL(1, 4), AC_HUFF_VAL(15, 1), AC_HUFF_VAL(14, 1),
+	AC_HUFF_VAL(4, 2), AC_HUFF_VAL(0, 11), AC_HUFF_VAL(8, 2), AC_HUFF_VAL(4, 3), AC_HUFF_VAL(0, 10),
+	AC_HUFF_VAL(2, 4), AC_HUFF_VAL(7, 2), AC_HUFF_VAL(21, 1), AC_HUFF_VAL(20, 1), AC_HUFF_VAL(0, 9),
+	AC_HUFF_VAL(19, 1), AC_HUFF_VAL(18, 1), AC_HUFF_VAL(1, 5), AC_HUFF_VAL(3, 3), AC_HUFF_VAL(0, 8),
+	AC_HUFF_VAL(6, 2), AC_HUFF_VAL(17, 1), AC_HUFF_VAL(10, 2), AC_HUFF_VAL(9, 2), AC_HUFF_VAL(5, 3),
+	AC_HUFF_VAL(3, 4), AC_HUFF_VAL(2, 5), AC_HUFF_VAL(1, 7), AC_HUFF_VAL(1, 6), AC_HUFF_VAL(0, 15),
+	AC_HUFF_VAL(0, 14), AC_HUFF_VAL(0, 13), AC_HUFF_VAL(0, 12), AC_HUFF_VAL(26, 1), AC_HUFF_VAL(25, 1),
+	AC_HUFF_VAL(24, 1), AC_HUFF_VAL(23, 1), AC_HUFF_VAL(22, 1), AC_HUFF_VAL(0, 31), AC_HUFF_VAL(0, 30),
+	AC_HUFF_VAL(0, 29), AC_HUFF_VAL(0, 28), AC_HUFF_VAL(0, 27), AC_HUFF_VAL(0, 26), AC_HUFF_VAL(0, 25),
+	AC_HUFF_VAL(0, 24), AC_HUFF_VAL(0, 23), AC_HUFF_VAL(0, 22), AC_HUFF_VAL(0, 21), AC_HUFF_VAL(0, 20),
+	AC_HUFF_VAL(0, 19), AC_HUFF_VAL(0, 18), AC_HUFF_VAL(0, 17), AC_HUFF_VAL(0, 16), AC_HUFF_VAL(0, 40),
+	AC_HUFF_VAL(0, 39), AC_HUFF_VAL(0, 38), AC_HUFF_VAL(0, 37), AC_HUFF_VAL(0, 36), AC_HUFF_VAL(0, 35),
+	AC_HUFF_VAL(0, 34), AC_HUFF_VAL(0, 33), AC_HUFF_VAL(0, 32), AC_HUFF_VAL(1, 14), AC_HUFF_VAL(1, 13),
+	AC_HUFF_VAL(1, 12), AC_HUFF_VAL(1, 11), AC_HUFF_VAL(1, 10), AC_HUFF_VAL(1, 9), AC_HUFF_VAL(1, 8),
+	AC_HUFF_VAL(1, 18), AC_HUFF_VAL(1, 17), AC_HUFF_VAL(1, 16), AC_HUFF_VAL(1, 15), AC_HUFF_VAL(6, 3),
+	AC_HUFF_VAL(16, 2), AC_HUFF_VAL(15, 2), AC_HUFF_VAL(14, 2), AC_HUFF_VAL(13, 2), AC_HUFF_VAL(12, 2),
+	AC_HUFF_VAL(11, 2), AC_HUFF_VAL(31, 1), AC_HUFF_VAL(30, 1), AC_HUFF_VAL(29, 1), AC_HUFF_VAL(28, 1),
+	AC_HUFF_VAL(27, 1),
 
 	// Escape code
 	ESCAPE_CODE,
@@ -143,7 +179,9 @@ void PSXStreamDecoder::initCommon() {
 	_audStream = 0;
 	_surface = new Graphics::Surface();
 	_yBuffer = _cbBuffer = _crBuffer = 0;
-	_huffman = new Common::Huffman(0, CODE_COUNT, s_huffmanCodes, s_huffmanLengths, s_huffmanSymbols);
+	_acHuffman = new Common::Huffman(0, AC_CODE_COUNT, s_huffmanACCodes, s_huffmanACLengths, s_huffmanACSymbols);
+	_dcHuffmanChroma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCChromaCodes, s_huffmanDCChromaLengths, s_huffmanDCSymbols);
+	_dcHuffmanLuma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols);
 }
 
 #define RAW_CD_SECTOR_SIZE 2352
@@ -448,6 +486,9 @@ void PSXStreamDecoder::decodeFrame(Common::SeekableReadStream *frame) {
 	if (version != 2 && version != 3)
 		error("Unknown PSX stream frame version");
 
+	// Initalize default v3 DC here
+	_lastDC[0] = _lastDC[1] = _lastDC[2] = 0;
+
 	for (int mbX = 0; mbX < _macroBlocksW; mbX++)
 		for (int mbY = 0; mbY < _macroBlocksH; mbY++)
 			decodeMacroBlock(&bits, mbX, mbY, scale, version);
@@ -461,12 +502,12 @@ void PSXStreamDecoder::decodeMacroBlock(Common::BitStream *bits, int mbX, int mb
 	int pitchC = _macroBlocksW * 8;
 
 	// Note the strange order of red before blue
-	decodeBlock(bits, _crBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version);
-	decodeBlock(bits, _cbBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version);
-	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16, pitchY, scale, version);
-	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8, pitchY, scale, version);
-	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY, pitchY, scale, version);
-	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY + 8, pitchY, scale, version);
+	decodeBlock(bits, _crBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version, kPlaneV);
+	decodeBlock(bits, _cbBuffer + (mbY * pitchC + mbX) * 8, pitchC, scale, version, kPlaneU);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16, pitchY, scale, version, kPlaneY);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8, pitchY, scale, version, kPlaneY);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY, pitchY, scale, version, kPlaneY);
+	decodeBlock(bits, _yBuffer + (mbY * pitchY + mbX) * 16 + 8 * pitchY + 8, pitchY, scale, version, kPlaneY);
 }
 
 // Standard JPEG/MPEG zig zag table
@@ -503,6 +544,32 @@ void PSXStreamDecoder::dequantizeBlock(int *coefficients, float *block, uint16 s
 	}
 }
 
+int PSXStreamDecoder::readDC(Common::BitStream *bits, uint16 version, PlaneType plane) {
+	// Version 2 just has its coefficient as 10-bits
+	if (version == 2)
+		return readSignedCoefficient(bits);
+
+	// Version 3 has it stored as huffman codes as a difference from the previous DC value
+
+	Common::Huffman *huffman = (plane == kPlaneY) ? _dcHuffmanLuma : _dcHuffmanChroma;
+
+	uint32 symbol = huffman->getSymbol(*bits);
+	int dc = 0;
+
+	if (GET_DC_BITS(symbol) != 0) {
+		bool negative = (bits->getBit() == 0);
+		dc = bits->getBits(GET_DC_BITS(symbol) - 1);
+
+		if (negative)
+			dc -= GET_DC_NEG(symbol);
+		else
+			dc += GET_DC_POS(symbol);
+	}
+
+	_lastDC[plane] += dc * 4; // convert from 8-bit to 10-bit
+	return _lastDC[plane];
+}
+
 #define BLOCK_OVERFLOW_CHECK() \
 	if (count > 63) \
 		error("PSXStreamDecoder::readAC(): Too many coefficients")
@@ -515,7 +582,7 @@ void PSXStreamDecoder::readAC(Common::BitStream *bits, int *block) {
 	int count = 0;
 
 	while (!bits->eos()) {
-		uint32 symbol = _huffman->getSymbol(*bits);
+		uint32 symbol = _acHuffman->getSymbol(*bits);
 
 		if (symbol == ESCAPE_CODE) {
 			// The escape code!
@@ -529,15 +596,15 @@ void PSXStreamDecoder::readAC(Common::BitStream *bits, int *block) {
 			break;
 		} else {
 			// Normal huffman code
-			int zeroes = GET_ZEROES(symbol);
+			int zeroes = GET_AC_ZERO_RUN(symbol);
 			count += zeroes + 1;
 			BLOCK_OVERFLOW_CHECK();
 			block += zeroes;
 
 			if (bits->getBit())
-				*block++ = -GET_AC(symbol);
+				*block++ = -GET_AC_COEFFICIENT(symbol);
 			else
-				*block++ = GET_AC(symbol);
+				*block++ = GET_AC_COEFFICIENT(symbol);
 		}
 	}
 }
@@ -603,18 +670,11 @@ void PSXStreamDecoder::idct(float *dequantData, float *result) {
 	}
 }
 
-void PSXStreamDecoder::decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version) {
-	int dc;
-
-	if (version == 2) {
-		dc = readSignedCoefficient(bits);
-	} else {
-		// TODO
-		error("Unhandled PSX stream version 3 DC");
-	}
-
+void PSXStreamDecoder::decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane) {
+	// Version 2 just has signed 10 bits for DC
+	// Version 3 has them huffman coded
 	int coefficients[8 * 8];
-	coefficients[0] = dc; // Start us off with the DC
+	coefficients[0] = readDC(bits, version, plane);
 	readAC(bits, &coefficients[1]); // Read in the AC
 
 	// Dequantize
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index e8de507..4223f03 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -96,15 +96,26 @@ private:
 	Audio::QueuingAudioStream *_audStream;
 	void queueAudioFromSector(Common::SeekableReadStream *sector);
 
-	Common::Huffman *_huffman;
+	enum PlaneType {
+		kPlaneY = 0,
+		kPlaneU = 1,
+		kPlaneV = 2
+	};
+
 	uint16 _macroBlocksW, _macroBlocksH;
 	byte *_yBuffer, *_cbBuffer, *_crBuffer;
 	void decodeFrame(Common::SeekableReadStream *frame);
 	void decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version);
-	void decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version);
+	void decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane);
 
-	void dequantizeBlock(int *coefficients, float *block, uint16 scale);
 	void readAC(Common::BitStream *bits, int *block);
+	Common::Huffman *_acHuffman;
+
+	int readDC(Common::BitStream *bits, uint16 version, PlaneType plane);
+	Common::Huffman *_dcHuffmanLuma, *_dcHuffmanChroma;
+	int _lastDC[3];
+
+	void dequantizeBlock(int *coefficients, float *block, uint16 scale);
 	void idct(float *dequantData, float *result);
 	int readSignedCoefficient(Common::BitStream *bits);
 


Commit: 66cd8bdd68d9e0d14e40c9f55c06f8ea9cf2006b
    https://github.com/scummvm/scummvm/commit/66cd8bdd68d9e0d14e40c9f55c06f8ea9cf2006b
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:18-08:00

Commit Message:
VIDEO: Make PSX streams calculate frame timing solely from CD speed

BS2 videos now play at the proper rate and BS1 videos have improved a/v sync.

Changed paths:
    engines/sword1/animation.cpp
    video/psx_decoder.cpp
    video/psx_decoder.h



diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index 9e190e0..a1ace0f 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -410,8 +410,8 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *
 
 		if (Common::File::exists(filename)) {
 #ifdef USE_RGB_COLOR
-			// All BS1 PSX videos seem to be 15fps
-			Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(15);
+			// All BS1 PSX videos run the videos at 2x speed
+			Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x);
 			return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX);
 #else
 			GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK"));
diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
index a0705e8..7c04b7f 100644
--- a/video/psx_decoder.cpp
+++ b/video/psx_decoder.cpp
@@ -38,31 +38,6 @@
 
 namespace Video {
 
-PSXStreamDecoder::PSXStreamDecoder(Common::Rational frameRate) {
-	assert(frameRate != 0);
-	initCommon();
-	_frameRate = frameRate;
-	_frameCount = 0;
-	_speed = kCDUnk;
-}
-
-PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) {
-	assert(speed != kCDUnk);
-	assert(frameCount != 0);
-	initCommon();
-	_frameCount = frameCount;
-	_speed = speed;
-	// frame rate will be calculated in loadStream()
-}
-
-PSXStreamDecoder::~PSXStreamDecoder() {
-	close();
-	delete _surface;
-	delete _acHuffman;
-	delete _dcHuffmanLuma;
-	delete _dcHuffmanChroma;
-}
-
 // Here are the codes/lengths/symbols that are used for decoding
 // DC coefficients (version 3 frames only)
 
@@ -174,7 +149,7 @@ static const uint32 s_huffmanACSymbols[AC_CODE_COUNT] = {
 	END_OF_BLOCK
 };
 
-void PSXStreamDecoder::initCommon() {
+PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) : _nextFrameStartTime(0, speed), _frameCount(frameCount) {
 	_stream = 0;
 	_audStream = 0;
 	_surface = new Graphics::Surface();
@@ -184,6 +159,14 @@ void PSXStreamDecoder::initCommon() {
 	_dcHuffmanLuma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols);
 }
 
+PSXStreamDecoder::~PSXStreamDecoder() {
+	close();
+	delete _surface;
+	delete _acHuffman;
+	delete _dcHuffmanLuma;
+	delete _dcHuffmanChroma;
+}
+
 #define RAW_CD_SECTOR_SIZE 2352
 
 #define CDXA_TYPE_MASK     0x0E
@@ -227,14 +210,6 @@ bool PSXStreamDecoder::loadStream(Common::SeekableReadStream *stream) {
 	delete sector;
 	_stream->seek(0);
 
-	// Calculate frame rate based on CD speed
-	if (_speed != kCDUnk) {
-		// TODO: This algorithm is too basic and not accurate enough
-		// TODO: Count the number of sectors per frame to get a better estimate
-		_frameRate = Common::Rational(_speed * _frameCount, _stream->size() / RAW_CD_SECTOR_SIZE);
-		_frameRate.debugPrint(0, "Approximate PSX Stream Frame Rate:");
-	}
-
 	return true;
 }
 
@@ -267,7 +242,20 @@ uint32 PSXStreamDecoder::getElapsedTime() const {
 	//if (_audStream)
 	//	return _mixer->getSoundElapsedTime(_audHandle);
 
-	return FixedRateVideoDecoder::getElapsedTime();
+	return VideoDecoder::getElapsedTime();
+}
+
+uint32 PSXStreamDecoder::getTimeToNextFrame() const {
+	if (!isVideoLoaded() || endOfVideo())
+		return 0;
+
+	uint32 nextTimeMillis = _nextFrameStartTime.msecs();
+	uint32 elapsedTime = getElapsedTime();
+
+	if (elapsedTime > nextTimeMillis)
+		return 0;
+
+	return nextTimeMillis - elapsedTime;
 }
 
 #define VIDEO_DATA_CHUNK_SIZE   2016
@@ -276,9 +264,11 @@ uint32 PSXStreamDecoder::getElapsedTime() const {
 const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() {
 	Common::SeekableReadStream *sector = 0;
 	byte *partialFrame = 0;
+	int sectorsRead = 0;
 
 	while (!endOfVideo()) {
 		sector = readSector();
+		sectorsRead++;
 
 		if (!sector)
 			error("Corrupt PSX stream sector");
@@ -322,6 +312,15 @@ const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() {
 					if (_curFrame == 0)
 						_startTime = g_system->getMillis();
 
+					// Increase the time by the amount of sectors we read
+					// One may notice that this is still not the most precise
+					// method since a frame takes up the time its sectors took
+					// up instead of the amount of time it takes the next frame
+					// to be read from the sectors. The actual frame rate should
+					// be constant instead of variable, so the slight difference
+					// in a frame's showing time is negligible (1/150 of a second).
+					_nextFrameStartTime = _nextFrameStartTime.addFrames(sectorsRead);
+
 					return _surface;
 				}
 			} else
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index 4223f03..6218704 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -53,18 +53,16 @@ namespace Video {
  *  - sword1 (psx)
  *  - sword2 (psx)
  */
-class PSXStreamDecoder : public FixedRateVideoDecoder {
+class PSXStreamDecoder : public VideoDecoder {
 public:
 	// CD speed in sectors/second
 	// Calling code should use these enum values instead of the constants
 	enum CDSpeed {
-		kCDUnk = 0,
 		kCD1x = 75,
 		kCD2x = 150
 	};
 
-	PSXStreamDecoder(Common::Rational frameRate);
-	PSXStreamDecoder(CDSpeed speed, uint32 frameCount);
+	PSXStreamDecoder(CDSpeed speed, uint32 frameCount = 0);
 	virtual ~PSXStreamDecoder();
 
 	bool loadStream(Common::SeekableReadStream *stream);
@@ -75,22 +73,18 @@ public:
 	uint16 getHeight() const { return _surface->h; }
 	uint32 getFrameCount() const { return _frameCount; }
 	uint32 getElapsedTime() const;
+	uint32 getTimeToNextFrame() const;
 	const Graphics::Surface *decodeNextFrame();
 	Graphics::PixelFormat getPixelFormat() const { return _surface->format; }
 	bool endOfVideo() const { return _stream->pos() >= _stream->size(); }
 
-protected:
-	// Hardcoded frame rate
-	Common::Rational getFrameRate() const { return _frameRate; }
-
 private:
 	void initCommon();
 	Common::SeekableReadStream *_stream;
 	Graphics::Surface *_surface;
 
-	CDSpeed _speed;
 	uint32 _frameCount;
-	Common::Rational _frameRate;
+	Audio::Timestamp _nextFrameStartTime;
 
 	Audio::SoundHandle _audHandle;
 	Audio::QueuingAudioStream *_audStream;


Commit: 8fea4968909eb5f3d154ae7f2964afba20033c50
    https://github.com/scummvm/scummvm/commit/8fea4968909eb5f3d154ae7f2964afba20033c50
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:18-08:00

Commit Message:
VIDEO: Clarify which PSX streams we can play

Changed paths:
    video/psx_decoder.h



diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index 6218704..c8ad92c 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -48,6 +48,9 @@ namespace Video {
 
 /**
  * Decoder for PSX stream videos.
+ * This currently implements the most basic PSX stream format that is
+ * used by most games on the system. Special variants are not supported
+ * at this time.
  *
  * Video decoder used in engines:
  *  - sword1 (psx)


Commit: a352c3cc000fb13f0dae32e84a859a8fb5cb5966
    https://github.com/scummvm/scummvm/commit/a352c3cc000fb13f0dae32e84a859a8fb5cb5966
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:18-08:00

Commit Message:
SWORD1: Add support for the PSX demo videos

Changed paths:
    engines/sword1/animation.cpp



diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index a1ace0f..e25b986 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -88,8 +88,8 @@ static const char *const sequenceListPSX[20] = {
 	"candle1",
 	"geodrop1",
 	"vulture1",
-	"",
-	"" // no credits?
+	"", // not present
+	""  // credits are not a video
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -173,7 +173,7 @@ bool MoviePlayer::load(uint32 id) {
 		filename = Common::String::format("%s.smk", sequenceList[id]);
 		break;
 	case kVideoDecoderPSX:
-		filename = Common::String::format("%s.str", sequenceListPSX[id]);
+		filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]);
 
 		// Need to switch to true color
 		initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0);
@@ -406,7 +406,8 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *
 
 	// For the PSX version, we'll try the PlayStation stream files
 	if (vm->isPsx()) {
-		filename = Common::String(sequenceListPSX[id]) + ".str";
+		// The demo uses the normal file names
+		filename = ((vm->_systemVars.isDemo) ? Common::String(sequenceList[id]) : Common::String(sequenceListPSX[id])) + ".str";
 
 		if (Common::File::exists(filename)) {
 #ifdef USE_RGB_COLOR
@@ -450,9 +451,11 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *
 		return NULL;
 	}
 
-	Common::String buf = Common::String::format(_("Cutscene '%s' not found"), sequenceList[id]);
-	GUI::MessageDialog dialog(buf, _("OK"));
-	dialog.runModal();
+	if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) {
+		Common::String buf = Common::String::format(_("Cutscene '%s' not found"), sequenceList[id]);
+		GUI::MessageDialog dialog(buf, _("OK"));
+		dialog.runModal();
+	}
 
 	return NULL;
 }


Commit: 7a3e0ea45300ba950d71ba0eae9381a0b0869f01
    https://github.com/scummvm/scummvm/commit/7a3e0ea45300ba950d71ba0eae9381a0b0869f01
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-02-23T13:48:19-08:00

Commit Message:
SWORD1: Add some TODO's for PSX stream subtitles

And some other minor cleanup

Changed paths:
    engines/sword1/animation.cpp



diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index e25b986..a3d732e 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -88,7 +88,7 @@ static const char *const sequenceListPSX[20] = {
 	"candle1",
 	"geodrop1",
 	"vulture1",
-	"", // not present
+	"", // demo video not present
 	""  // credits are not a video
 };
 
@@ -102,8 +102,8 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::
 	_decoderType = decoderType;
 	_decoder = decoder;
 
-	_white = (decoderType == kVideoDecoderPSX) ? _system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff) : 255;
-	_black = (decoderType == kVideoDecoderPSX) ? _system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : 0;
+	_white = 255;
+	_black = 0;
 }
 
 MoviePlayer::~MoviePlayer() {
@@ -224,8 +224,9 @@ void MoviePlayer::play() {
 }
 
 void MoviePlayer::performPostProcessing(byte *screen) {
-	// TODO
-	if (_decoderType == kVideoDecoderPSX)
+	// TODO: We don't support the PSX stream videos yet
+	// nor using the PSX fonts to display subtitles.
+	if (_vm->isPsx())
 		return;
 
 	if (!_movieTexts.empty()) {
@@ -361,11 +362,11 @@ bool MoviePlayer::playVideo() {
 }
 
 uint32 MoviePlayer::getBlackColor() {
-	return _black;
+	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black;
 }
 
 uint32 MoviePlayer::getWhiteColor() {
-	return _white;
+	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF) : _white;
 }
 
 void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {


Commit: 4637d55337d083d85bea762db7d59b47e92c0fbe
    https://github.com/scummvm/scummvm/commit/4637d55337d083d85bea762db7d59b47e92c0fbe
Author: Willem Jan Palenstijn (wjp at usecode.org)
Date: 2012-02-23T13:49:59-08:00

Commit Message:
Merge pull request #171 from clone2727/psx-stream-2

This is a manual merge based on clone2727's merge of his branch
with the sword1 subtitle changes on master.

Changed paths:
  A video/psx_decoder.cpp
  A video/psx_decoder.h
    engines/sword1/animation.cpp
    engines/sword1/animation.h
    engines/sword1/sword1.cpp
    engines/sword2/animation.cpp
    engines/sword2/animation.h
    engines/sword2/function.cpp
    engines/sword2/sword2.cpp
    video/module.mk



diff --cc engines/sword1/animation.cpp
index e274e02,a3d732e..1e29640
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@@ -215,10 -256,10 +265,10 @@@ void MoviePlayer::performPostProcessing
  			for (x = 0; x < _textWidth; x++) {
  				switch (src[x]) {
  				case BORDER_COL:
- 					dst[x] = findBlackPalIndex();
+ 					dst[x] = getBlackColor();
  					break;
  				case LETTER_COL:
- 					dst[x] = findTextColorPalIndex();
 -					dst[x] = getWhiteColor();
++					dst[x] = findTextColor();
  					break;
  				}
  			}
@@@ -367,52 -353,39 +422,90 @@@ bool MoviePlayer::playVideo() 
  		_vm->_system->delayMillis(10);
  	}
  
- 	return !_vm->shouldQuit();
+ 	if (_decoderType == kVideoDecoderPSX) {
+ 		// Need to jump back to paletted color
+ 		initGraphics(g_system->getWidth(), g_system->getHeight(), true);
+ 	}
+ 
+ 	return !_vm->shouldQuit() && !skipped;
  }
  
- byte MoviePlayer::findBlackPalIndex() {
- 	return _black;
+ uint32 MoviePlayer::getBlackColor() {
+ 	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black;
  }
- 	
- byte MoviePlayer::findTextColorPalIndex() {
+ 
 -uint32 MoviePlayer::getWhiteColor() {
 -	return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF) : _white;
++uint32 MoviePlayer::findTextColor() {
++	if (_decoderType == kVideoDecoderPSX) {
++		// We're in true color mode, so return the actual colors
++		switch (_textColor) {
++		case 1:
++			return g_system->getScreenFormat().RGBToColor(248, 252, 248);
++		case 2:
++			return g_system->getScreenFormat().RGBToColor(184, 188, 184);
++		case 3:
++			return g_system->getScreenFormat().RGBToColor(200, 120, 184);
++		case 4:
++			return g_system->getScreenFormat().RGBToColor(80, 152, 184);
++		}
++
++		return g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF);
++	}
++
 +	switch (_textColor) {
 +	case 1:
 +		return _c1Color;
 +	case 2:
 +		return _c2Color;
 +	case 3:
 +		return _c3Color;
 +	case 4:
 +		return _c4Color;
 +	}
 +	return _c1Color;
 +}
 +
 +void MoviePlayer::convertColor(byte r, byte g, byte b, float &h, float &s, float &v) {
 +	float varR = r / 255.0f;
 +	float varG = g / 255.0f;
 +	float varB = b / 255.0f;
 +
 +	float min = MIN(varR, MIN(varG, varB));
 +	float max = MAX(varR, MAX(varG, varB));
 +
 +	v = max;
 +	float d = max - min;
 +	s = max == 0.0f ? 0.0f : d / max;
 +
 +	if (min == max) {
 +		h = 0.0f; // achromatic
 +	} else {
 +		if (max == varR)
 +			h = (varG - varB) / d + (varG < varB ? 6.0f : 0.0f);
 +		else if (max == varG)
 +			h = (varB - varR) / d + 2.0f;
 +		else
 +			h = (varR - varG) / d + 4.0f;
 +		h /= 6.0f;
 +	}
  }
  
+ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {
+ 	// The PSX videos have half resolution
+ 
+ 	Graphics::Surface scaledFrame;
+ 	scaledFrame.create(frame->w, frame->h * 2, frame->format);
+ 
+ 	for (int y = 0; y < scaledFrame.h; y++)
+ 		memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel);
+ 
+ 	uint16 x = (g_system->getWidth() - scaledFrame.w) / 2;
+ 	uint16 y = (g_system->getHeight() - scaledFrame.h) / 2;
+ 
+ 	_vm->_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
+ 
+ 	scaledFrame.free();
+ }
+ 
  DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
  	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
  }
diff --cc engines/sword1/animation.h
index c436607,23dae7f..f64b03d
--- a/engines/sword1/animation.h
+++ b/engines/sword1/animation.h
@@@ -82,9 -80,7 +82,9 @@@ protected
  	OSystem *_system;
  	Common::List<MovieText> _movieTexts;
  	int _textX, _textY, _textWidth, _textHeight;
 -	uint32 _white, _black;
 +	int _textColor;
- 	byte _black;
- 	byte _c1Color, _c2Color, _c3Color, _c4Color;
++	uint32 _black;
++	uint32 _c1Color, _c2Color, _c3Color, _c4Color;
  	DecoderType _decoderType;
  
  	Video::VideoDecoder *_decoder;
@@@ -93,10 -89,10 +93,11 @@@
  
  	bool playVideo();
  	void performPostProcessing(byte *screen);
+ 	void drawFramePSX(const Graphics::Surface *frame);
  
- 	byte findBlackPalIndex();
- 	byte findTextColorPalIndex();
+ 	uint32 getBlackColor();
 -	uint32 getWhiteColor();
++	uint32 findTextColor();
 +	void convertColor(byte r, byte g, byte b, float &h, float &s, float &v);
  };
  
  MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system);






More information about the Scummvm-git-logs mailing list