diff options
| -rw-r--r-- | engines/agos/animation.cpp | 12 | ||||
| -rw-r--r-- | engines/agos/animation.h | 4 | ||||
| -rw-r--r-- | engines/saga/introproc_saga2.cpp | 4 | ||||
| -rw-r--r-- | engines/scumm/he/animation_he.cpp | 5 | ||||
| -rw-r--r-- | engines/sword1/animation.cpp | 9 | ||||
| -rw-r--r-- | engines/sword2/animation.cpp | 9 | ||||
| -rw-r--r-- | engines/toon/movie.cpp | 35 | ||||
| -rw-r--r-- | engines/toon/movie.h | 14 | ||||
| -rw-r--r-- | engines/toon/toon.cpp | 2 | ||||
| -rw-r--r-- | video/smk_decoder.cpp | 391 | ||||
| -rw-r--r-- | video/smk_decoder.h | 117 | 
11 files changed, 338 insertions, 264 deletions
| diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 10c01741ae..3e8488d7d5 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -415,7 +415,7 @@ void MoviePlayerDXA::updateBalance() {  MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) -	: MoviePlayer(vm), SmackerDecoder(vm->_mixer) { +	: MoviePlayer(vm), SmackerDecoder() {  	debug(0, "Creating SMK cutscene player");  	memset(baseName, 0, sizeof(baseName)); @@ -431,12 +431,12 @@ bool MoviePlayerSMK::load() {  	if (!loadStream(videoStream))  		error("Failed to load video stream from file %s", videoName.c_str()); +	start(); +  	debug(0, "Playing video %s", videoName.c_str());  	CursorMan.showMouse(false); -	_firstFrameOffset = _fileStream->pos(); -  	return true;  } @@ -477,10 +477,8 @@ void MoviePlayerSMK::handleNextFrame() {  }  void MoviePlayerSMK::nextFrame() { -	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { -		_fileStream->seek(_firstFrameOffset); -		_curFrame = -1; -	} +	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) +		rewind();  	if (!endOfVideo()) {  		decodeNextFrame(); diff --git a/engines/agos/animation.h b/engines/agos/animation.h index d1ff074b03..37a666b201 100644 --- a/engines/agos/animation.h +++ b/engines/agos/animation.h @@ -67,9 +67,6 @@ protected:  	virtual void handleNextFrame();  	virtual bool processFrame() = 0;  	virtual void startSound() {} - -protected: -	uint32 _firstFrameOffset;  };  class MoviePlayerDXA : public MoviePlayer, Video::DXADecoder { @@ -93,6 +90,7 @@ private:  	bool processFrame();  	void startSound();  	void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch); +	uint32 _firstFrameOffset;  };  class MoviePlayerSMK : public MoviePlayer, Video::SmackerDecoder { diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp index b6470370af..15f7f4dc15 100644 --- a/engines/saga/introproc_saga2.cpp +++ b/engines/saga/introproc_saga2.cpp @@ -92,7 +92,7 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) {  }  void Scene::playMovie(const char *filename) { -	Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(_vm->_mixer); +	Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();  	if (!smkDecoder->loadFile(filename))  		return; @@ -101,6 +101,8 @@ void Scene::playMovie(const char *filename) {  	uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;  	bool skipVideo = false; +	smkDecoder->start(); +  	while (!_vm->shouldQuit() && !smkDecoder->endOfVideo() && !skipVideo) {  		if (smkDecoder->needsUpdate()) {  			const Graphics::Surface *frame = smkDecoder->decodeNextFrame(); diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp index 40e99c26a8..b37a565aab 100644 --- a/engines/scumm/he/animation_he.cpp +++ b/engines/scumm/he/animation_he.cpp @@ -40,7 +40,10 @@ MoviePlayer::MoviePlayer(ScummEngine_v90he *vm, Audio::Mixer *mixer) : _vm(vm) {  		_video = new Video::BinkDecoder();  	else  #endif -		_video = new Video::SmackerDecoder(mixer); +	{ +		_video = new Video::SmackerDecoder(); +		((Video::AdvancedVideoDecoder *)_video)->start(); +	}  	_flags = 0;  	_wizResNum = 0; diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index 49c5ef7312..8f863d1e09 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -179,6 +179,13 @@ bool MoviePlayer::load(uint32 id) {  		break;  	case kVideoDecoderSMK:  		filename = Common::String::format("%s.smk", sequenceList[id]); + +		if (_decoder->loadFile(filename)) { +			((Video::AdvancedVideoDecoder *)_decoder)->start(); // TODO: Remove after new API is complete +			return true; +		} else { +			return false; +		}  		break;  	case kVideoDecoderPSX:  		filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]); @@ -547,7 +554,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *  	filename = Common::String::format("%s.smk", sequenceList[id]);  	if (Common::File::exists(filename)) { -		Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); +		Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();  		return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK);  	} diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index c1cf33ff09..e257ec9029 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -89,6 +89,13 @@ bool MoviePlayer::load(const char *name) {  		break;  	case kVideoDecoderSMK:  		filename = Common::String::format("%s.smk", name); + +		if (_decoder->loadFile(filename)) { +			((Video::AdvancedVideoDecoder *)_decoder)->start(); // TODO: Remove after new API is complete +			return true; +		} else { +			return false; +		}  		break;  	case kVideoDecoderPSX:  		filename = Common::String::format("%s.str", name); @@ -442,7 +449,7 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s  	filename = Common::String::format("%s.smk", name);  	if (Common::File::exists(filename)) { -		Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); +		Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();  		return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK);  	} diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index 93e41adf57..d988a3ed60 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -33,6 +33,10 @@  namespace Toon { +ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() { +	_lowRes = false; +} +  void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {  	debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize); @@ -40,33 +44,21 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui  		/* uint16 width = */ _fileStream->readUint16LE();  		uint16 height = _fileStream->readUint16LE();  		_lowRes = (height == getHeight() / 2); -	} else +	} else {  		Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); +	}  } -bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) { -	debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str()); +bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) { +	if (!Video::SmackerDecoder::loadStream(stream)) +		return false;  	_lowRes = false; - -	if (Video::SmackerDecoder::loadFile(filename)) { -		if (_surface->h == 200) { -			if (_surface) { -				_surface->free(); -				delete _surface; -			} -			_surface = new Graphics::Surface(); -			_surface->create(640, 400, Graphics::PixelFormat::createFormatCLUT8()); -			_header.flags = 4; -		} - -		return true; -	} -	return false; +	return true;  } -ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Video::SmackerDecoder(mixer, soundType) { -	_lowRes = false; +Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { +	return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, signature);  }  // decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed @@ -103,6 +95,9 @@ void Movie::play(const Common::String &video, int32 flags) {  bool Movie::playVideo(bool isFirstIntroVideo) {  	debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo); + +	_decoder->start(); +  	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {  		if (_decoder->needsUpdate()) {  			const Graphics::Surface *frame = _decoder->decodeNextFrame(); diff --git a/engines/toon/movie.h b/engines/toon/movie.h index 2cd33302f2..e795182cba 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -30,13 +30,17 @@ namespace Toon {  class ToonstruckSmackerDecoder : public Video::SmackerDecoder {  public: -	ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); -	virtual ~ToonstruckSmackerDecoder() {} -	void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); -	bool loadFile(const Common::String &filename); +	ToonstruckSmackerDecoder(); + +	bool loadStream(Common::SeekableReadStream *stream);  	bool isLowRes() { return _lowRes; } +  protected: -	bool _lowRes; +	void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); +	SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + +private: +	bool _lowRes;	  };  class Movie { diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index ee427652d8..9fd8415676 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -51,7 +51,7 @@ void ToonEngine::init() {  	_currentScriptRegion = 0;  	_resources = new Resources(this);  	_animationManager = new AnimationManager(this); -	_moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer)); +	_moviePlayer = new Movie(this, new ToonstruckSmackerDecoder());  	_hotspots = new Hotspots(this);  	_mainSurface = new Graphics::Surface(); diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp index 359f4cb9bd..d707ad519f 100644 --- a/video/smk_decoder.cpp +++ b/video/smk_decoder.cpp @@ -204,8 +204,7 @@ BigHuffmanTree::BigHuffmanTree(Common::BitStream &bs, int allocSize)  	delete _hiBytes;  } -BigHuffmanTree::~BigHuffmanTree() -{ +BigHuffmanTree::~BigHuffmanTree() {  	delete[] _tree;  } @@ -278,24 +277,17 @@ uint32 BigHuffmanTree::getCode(Common::BitStream &bs) {  	return v;  } -SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) -	: _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) { -	_surface = 0; +SmackerDecoder::SmackerDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) {  	_fileStream = 0; -	_dirtyPalette = false; +	_firstFrameStart = 0; +	_frameTypes = 0; +	_frameSizes = 0;  }  SmackerDecoder::~SmackerDecoder() {  	close();  } -uint32 SmackerDecoder::getTime() const { -	if (_audioStream && _audioStarted) -		return _mixer->getSoundElapsedTime(_audioHandle); - -	return FixedRateVideoDecoder::getTime(); -} -  bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {  	close(); @@ -309,16 +301,17 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {  	uint32 width = _fileStream->readUint32LE();  	uint32 height = _fileStream->readUint32LE(); -	_frameCount = _fileStream->readUint32LE(); -	int32 frameRate = _fileStream->readSint32LE(); - -	// framerate contains 2 digits after the comma, so 1497 is actually 14.97 fps -	if (frameRate > 0) -		_frameRate = Common::Rational(1000, frameRate); -	else if (frameRate < 0) -		_frameRate = Common::Rational(100000, -frameRate); +	uint32 frameCount = _fileStream->readUint32LE(); +	int32 frameDelay = _fileStream->readSint32LE(); + +	// frame rate contains 2 digits after the comma, so 1497 is actually 14.97 fps +	Common::Rational frameRate; +	if (frameDelay > 0) +		frameRate = Common::Rational(1000, frameDelay); +	else if (frameDelay < 0) +		frameRate = Common::Rational(100000, -frameDelay);  	else -		_frameRate = 1000; +		frameRate = 1000;  	// Flags are determined by which bit is set, which can be one of the following:  	// 0 - set to 1 if file contains a ring frame. @@ -328,6 +321,9 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {      // before it is displayed.  	_header.flags = _fileStream->readUint32LE(); +	SmackerVideoTrack *videoTrack = createVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature); +	addTrack(videoTrack); +  	// TODO: should we do any extra processing for Smacker files with ring frames?  	// TODO: should we do any extra processing for Y-doubled videos? Are they the @@ -374,92 +370,78 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {  				warning("Unhandled Smacker v2 audio compression");  			if (i == 0) -				_audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo); +				addTrack(new SmackerAudioTrack(_header.audioInfo[i], _soundType));  		}  	}  	_header.dummy = _fileStream->readUint32LE(); -	_frameSizes = new uint32[_frameCount]; -	for (i = 0; i < _frameCount; ++i) +	_frameSizes = new uint32[frameCount]; +	for (i = 0; i < frameCount; ++i)  		_frameSizes[i] = _fileStream->readUint32LE(); -	_frameTypes = new byte[_frameCount]; -	for (i = 0; i < _frameCount; ++i) +	_frameTypes = new byte[frameCount]; +	for (i = 0; i < frameCount; ++i)  		_frameTypes[i] = _fileStream->readByte();  	byte *huffmanTrees = (byte *) malloc(_header.treesSize);  	_fileStream->read(huffmanTrees, _header.treesSize);  	Common::BitStream8LSB bs(new Common::MemoryReadStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), true); +	videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize); -	_MMapTree = new BigHuffmanTree(bs, _header.mMapSize); -	_MClrTree = new BigHuffmanTree(bs, _header.mClrSize); -	_FullTree = new BigHuffmanTree(bs, _header.fullSize); -	_TypeTree = new BigHuffmanTree(bs, _header.typeSize); - -	_surface = new Graphics::Surface(); +	_firstFrameStart = _fileStream->pos(); -	// Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) -	_surface->create(width, height * (_header.flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8()); - -	memset(_palette, 0, 3 * 256);  	return true;  }  void SmackerDecoder::close() { -	if (!_fileStream) -		return; - -	if (_audioStream) { -		if (_audioStarted) { -			// The mixer will delete the stream. -			_mixer->stopHandle(_audioHandle); -			_audioStarted = false; -		} else { -			delete _audioStream; -		} -		_audioStream = 0; -	} +	AdvancedVideoDecoder::close();  	delete _fileStream;  	_fileStream = 0; -	_surface->free(); -	delete _surface; -	_surface = 0; - -	delete _MMapTree; -	delete _MClrTree; -	delete _FullTree; -	delete _TypeTree; +	delete[] _frameTypes; +	_frameTypes = 0;  	delete[] _frameSizes; -	delete[] _frameTypes; +	_frameSizes = 0; +} -	reset(); +bool SmackerDecoder::rewind() { +	// Call the parent method to rewind the tracks first +	// In particular, only videos without sound can be rewound +	if (!AdvancedVideoDecoder::rewind()) +		return false; + +	// And seek back to where the first frame begins +	_fileStream->seek(_firstFrameStart); +	return true;  } -const Graphics::Surface *SmackerDecoder::decodeNextFrame() { +void SmackerDecoder::readNextPacket() { +	SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0); + +	if (videoTrack->endOfTrack()) +		return; + +	videoTrack->increaseCurFrame(); +  	uint i;  	uint32 chunkSize = 0;  	uint32 dataSizeUnpacked = 0;  	uint32 startPos = _fileStream->pos(); -	_curFrame++; -  	// Check if we got a frame with palette data, and  	// call back the virtual setPalette function to set  	// the current palette -	if (_frameTypes[_curFrame] & 1) { -		unpackPalette(); -		_dirtyPalette = true; -	} +	if (_frameTypes[videoTrack->getCurFrame()] & 1) +		videoTrack->unpackPalette(_fileStream);  	// Load audio tracks  	for (i = 0; i < 7; ++i) { -		if (!(_frameTypes[_curFrame] & (2 << i))) +		if (!(_frameTypes[videoTrack->getCurFrame()] & (2 << i)))  			continue;  		chunkSize = _fileStream->readUint32LE(); @@ -475,29 +457,109 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {  		handleAudioTrack(i, chunkSize, dataSizeUnpacked);  	} -	uint32 frameSize = _frameSizes[_curFrame] & ~3; -//	uint32 remainder =  _frameSizes[_curFrame] & 3; +	uint32 frameSize = _frameSizes[videoTrack->getCurFrame()] & ~3; +//	uint32 remainder =  _frameSizes[videoTrack->getCurFrame()] & 3;  	if (_fileStream->pos() - startPos > frameSize)  		error("Smacker actual frame size exceeds recorded frame size");  	uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); -	_frameData = (byte *)malloc(frameDataSize + 1); +	byte *frameData = (byte *)malloc(frameDataSize + 1);  	// Padding to keep the BigHuffmanTrees from reading past the data end -	_frameData[frameDataSize] = 0x00; +	frameData[frameDataSize] = 0x00; + +	_fileStream->read(frameData, frameDataSize); -	_fileStream->read(_frameData, frameDataSize); +	Common::BitStream8LSB bs(new Common::MemoryReadStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), true); +	videoTrack->decodeFrame(bs); -	Common::BitStream8LSB bs(new Common::MemoryReadStream(_frameData, frameDataSize + 1, DisposeAfterUse::YES), true); +	_fileStream->seek(startPos + frameSize); +} +void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { +	if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) { +		// Get the audio track, which start at offset 1 (first track is video) +		SmackerAudioTrack *audioTrack = (SmackerAudioTrack *)getTrack(track + 1); + +		// If it's track 0, play the audio data +		byte *soundBuffer = (byte *)malloc(chunkSize + 1); +		// Padding to keep the SmallHuffmanTrees from reading past the data end +		soundBuffer[chunkSize] = 0x00; + +		_fileStream->read(soundBuffer, chunkSize); + +		if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) { +			// TODO: Compressed audio (Bink RDFT/DCT encoded) +			free(soundBuffer); +			return; +		} else if (_header.audioInfo[track].compression == kCompressionDPCM) { +			// Compressed audio (Huffman DPCM encoded) +			audioTrack->queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize); +			free(soundBuffer); +		} else { +			// Uncompressed audio (PCM) +			audioTrack->queuePCM(soundBuffer, chunkSize); +		} +	} else { +		// Ignore the rest of the audio tracks, if they exist +		// TODO: Are there any Smacker videos with more than one audio stream? +		// If yes, we should play the rest of the audio streams as well +		if (chunkSize > 0) +			_fileStream->skip(chunkSize); +	} +} + +SmackerDecoder::SmackerVideoTrack::SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) { +	_surface = new Graphics::Surface(); +	_surface->create(width, height * (flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8()); +	_frameCount = frameCount; +	_frameRate = frameRate; +	_flags = flags; +	_signature = signature; +	_curFrame = -1; +	_dirtyPalette = false; +	_MMapTree = _MClrTree = _FullTree = _TypeTree = 0; +	memset(_palette, 0, 3 * 256); +} + +SmackerDecoder::SmackerVideoTrack::~SmackerVideoTrack() { +	_surface->free(); +	delete _surface; + +	delete _MMapTree; +	delete _MClrTree; +	delete _FullTree; +	delete _TypeTree; +} + +uint16 SmackerDecoder::SmackerVideoTrack::getWidth() const { +	return _surface->w; +} + +uint16 SmackerDecoder::SmackerVideoTrack::getHeight() const { +	return _surface->h; +} + +Graphics::PixelFormat SmackerDecoder::SmackerVideoTrack::getPixelFormat() const { +	return _surface->format; +} + +void SmackerDecoder::SmackerVideoTrack::readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) { +	_MMapTree = new BigHuffmanTree(bs, mMapSize); +	_MClrTree = new BigHuffmanTree(bs, mClrSize); +	_FullTree = new BigHuffmanTree(bs, fullSize); +	_TypeTree = new BigHuffmanTree(bs, typeSize); +} + +void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) {  	_MMapTree->reset();  	_MClrTree->reset();  	_FullTree->reset();  	_TypeTree->reset();  	// Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) -	uint doubleY = _header.flags ? 2 : 1; +	uint doubleY = _flags ? 2 : 1;  	uint bw = getWidth() / 4;  	uint bh = getHeight() / doubleY / 4; @@ -508,6 +570,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {  	uint type, run, j, mode;  	uint32 p1, p2, clr, map;  	byte hi, lo; +	uint i;  	while (block < blocks) {  		type = _TypeTree->getCode(bs); @@ -536,7 +599,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {  			break;  		case SMK_BLOCK_FULL:  			// Smacker v2 has one mode, Smacker v4 has three -			if (_header.signature == MKTAG('S','M','K','2')) { +			if (_signature == MKTAG('S','M','K','2')) {  				mode = 0;  			} else {  				// 00 - mode 0 @@ -628,60 +691,75 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {  			break;  		}  	} +} -	_fileStream->seek(startPos + frameSize); +void SmackerDecoder::SmackerVideoTrack::unpackPalette(Common::SeekableReadStream *stream) { +	uint startPos = stream->pos(); +	uint32 len = 4 * stream->readByte(); -	if (_curFrame == 0) -		_startTime = g_system->getMillis(); +	byte *chunk = (byte *)malloc(len); +	stream->read(chunk, len); +	byte *p = chunk; -	return _surface; -} +	byte oldPalette[3 * 256]; +	memcpy(oldPalette, _palette, 3 * 256); -void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { -	if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) { -		// If it's track 0, play the audio data -		byte *soundBuffer = (byte *)malloc(chunkSize + 1); -		// Padding to keep the SmallHuffmanTrees from reading past the data end -		soundBuffer[chunkSize] = 0x00; +	byte *pal = _palette; -		_fileStream->read(soundBuffer, chunkSize); +	int sz = 0; +	byte b0; +	while (sz < 256) { +		b0 = *p++; +		if (b0 & 0x80) {               // if top bit is 1 (0x80 = 10000000) +			sz += (b0 & 0x7f) + 1;     // get lower 7 bits + 1 (0x7f = 01111111) +			pal += 3 * ((b0 & 0x7f) + 1); +		} else if (b0 & 0x40) {        // if top 2 bits are 01 (0x40 = 01000000) +			byte c = (b0 & 0x3f) + 1;  // get lower 6 bits + 1 (0x3f = 00111111) +			uint s = 3 * *p++; +			sz += c; -		if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) { -			// TODO: Compressed audio (Bink RDFT/DCT encoded) -			free(soundBuffer); -			return; -		} else if (_header.audioInfo[track].compression == kCompressionDPCM) { -			// Compressed audio (Huffman DPCM encoded) -			queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize, track); -			free(soundBuffer); -		} else { -			// Uncompressed audio (PCM) -			byte flags = 0; -			if (_header.audioInfo[track].is16Bits) -				flags = flags | Audio::FLAG_16BITS; -			if (_header.audioInfo[track].isStereo) -				flags = flags | Audio::FLAG_STEREO; - -			_audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags); -			// The sound buffer will be deleted by QueuingAudioStream -		} +			while (c--) { +				*pal++ = oldPalette[s + 0]; +				*pal++ = oldPalette[s + 1]; +				*pal++ = oldPalette[s + 2]; +				s += 3; +			} +		} else {                       // top 2 bits are 00 +			sz++; +			// get the lower 6 bits for each component (0x3f = 00111111) +			byte b = b0 & 0x3f; +			byte g = (*p++) & 0x3f; +			byte r = (*p++) & 0x3f; + +			assert(g < 0xc0 && b < 0xc0); -		if (!_audioStarted) { -			_mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); -			_audioStarted = true; +			// upscale to full 8-bit color values by multiplying by 4 +			*pal++ = b * 4; +			*pal++ = g * 4; +			*pal++ = r * 4;  		} -	} else { -		// Ignore the rest of the audio tracks, if they exist -		// TODO: Are there any Smacker videos with more than one audio stream? -		// If yes, we should play the rest of the audio streams as well -		if (chunkSize > 0) -			_fileStream->skip(chunkSize);  	} + +	stream->seek(startPos + len); +	free(chunk); + +	_dirtyPalette = true;  } -void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, -		uint32 unpackedSize, int streamNum) { +SmackerDecoder::SmackerAudioTrack::SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType) : +		_audioInfo(audioInfo), _soundType(soundType) { +	_audioStream = Audio::makeQueuingAudioStream(_audioInfo.sampleRate, _audioInfo.isStereo); +} +SmackerDecoder::SmackerAudioTrack::~SmackerAudioTrack() { +	delete _audioStream; +} + +Audio::AudioStream *SmackerDecoder::SmackerAudioTrack::getAudioStream() const { +	return _audioStream; +} + +void SmackerDecoder::SmackerAudioTrack::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize) {  	Common::BitStream8LSB audioBS(new Common::MemoryReadStream(buffer, bufferSize), true);  	bool dataPresent = audioBS.getBit(); @@ -689,9 +767,9 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,  		return;  	bool isStereo = audioBS.getBit(); -	assert(isStereo == _header.audioInfo[streamNum].isStereo); +	assert(isStereo == _audioInfo.isStereo);  	bool is16Bits = audioBS.getBit(); -	assert(is16Bits == _header.audioInfo[streamNum].is16Bits); +	assert(is16Bits == _audioInfo.is16Bits);  	int numBytes = 1 * (isStereo ? 2 : 1) * (is16Bits ? 2 : 1); @@ -759,74 +837,21 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,  	for (int k = 0; k < numBytes; k++)  		delete audioTrees[k]; -	byte flags = 0; -	if (_header.audioInfo[0].is16Bits) -		flags = flags | Audio::FLAG_16BITS; -	if (_header.audioInfo[0].isStereo) -		flags = flags | Audio::FLAG_STEREO; -	_audioStream->queueBuffer(unpackedBuffer, unpackedSize, DisposeAfterUse::YES, flags); -	// unpackedBuffer will be deleted by QueuingAudioStream +	queuePCM(unpackedBuffer, unpackedSize);  } -void SmackerDecoder::unpackPalette() { -	uint startPos = _fileStream->pos(); -	uint32 len = 4 * _fileStream->readByte(); - -	byte *chunk = (byte *)malloc(len); -	_fileStream->read(chunk, len); -	byte *p = chunk; - -	byte oldPalette[3*256]; -	memcpy(oldPalette, _palette, 3 * 256); - -	byte *pal = _palette; - -	int sz = 0; -	byte b0; -	while (sz < 256) { -		b0 = *p++; -		if (b0 & 0x80) {               // if top bit is 1 (0x80 = 10000000) -			sz += (b0 & 0x7f) + 1;     // get lower 7 bits + 1 (0x7f = 01111111) -			pal += 3 * ((b0 & 0x7f) + 1); -		} else if (b0 & 0x40) {        // if top 2 bits are 01 (0x40 = 01000000) -			byte c = (b0 & 0x3f) + 1;  // get lower 6 bits + 1 (0x3f = 00111111) -			uint s = 3 * *p++; -			sz += c; - -			while (c--) { -				*pal++ = oldPalette[s + 0]; -				*pal++ = oldPalette[s + 1]; -				*pal++ = oldPalette[s + 2]; -				s += 3; -			} -		} else {                       // top 2 bits are 00 -			sz++; -			// get the lower 6 bits for each component (0x3f = 00111111) -			byte b = b0 & 0x3f; -			byte g = (*p++) & 0x3f; -			byte r = (*p++) & 0x3f; - -			assert(g < 0xc0 && b < 0xc0); - -			// upscale to full 8-bit color values by multiplying by 4 -			*pal++ = b * 4; -			*pal++ = g * 4; -			*pal++ = r * 4; -		} -	} - -	_fileStream->seek(startPos + len); -	free(chunk); -} +void SmackerDecoder::SmackerAudioTrack::queuePCM(byte *buffer, uint32 bufferSize) { +	byte flags = 0; +	if (_audioInfo.is16Bits) +		flags |= Audio::FLAG_16BITS; +	if (_audioInfo.isStereo) +		flags |= Audio::FLAG_STEREO; -void SmackerDecoder::updateVolume() { -	if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) -		g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +	_audioStream->queueBuffer(buffer, bufferSize, DisposeAfterUse::YES, flags);  } -void SmackerDecoder::updateBalance() { -	if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) -		g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +SmackerDecoder::SmackerVideoTrack *SmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { +	return new SmackerVideoTrack(width, height, frameCount, frameRate, flags, signature);  }  } // End of namespace Video diff --git a/video/smk_decoder.h b/video/smk_decoder.h index 516882e7c8..78a4ded0fc 100644 --- a/video/smk_decoder.h +++ b/video/smk_decoder.h @@ -34,6 +34,7 @@ class QueuingAudioStream;  }  namespace Common { +class BitStream;  class SeekableReadStream;  } @@ -56,42 +57,72 @@ class BigHuffmanTree;   *  - sword2   *  - toon   */ -class SmackerDecoder : public FixedRateVideoDecoder { +class SmackerDecoder : public AdvancedVideoDecoder {  public: -	SmackerDecoder(Audio::Mixer *mixer, -			Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); +	SmackerDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);  	virtual ~SmackerDecoder(); -	bool loadStream(Common::SeekableReadStream *stream); +	virtual bool loadStream(Common::SeekableReadStream *stream);  	void close(); -	bool isVideoLoaded() const { return _fileStream != 0; } -	uint16 getWidth() const { return _surface->w; } -	uint16 getHeight() const { return _surface->h; } -	uint32 getFrameCount() const { return _frameCount; } -	uint32 getTime() const; -	const Graphics::Surface *decodeNextFrame(); -	Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } -	const byte *getPalette() { _dirtyPalette = false; return _palette; } -	bool hasDirtyPalette() const { return _dirtyPalette; } -	virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); +	bool rewind();  protected: -	Common::SeekableReadStream *_fileStream; +	void readNextPacket(); + +	virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); -	// VideoDecoder API -	void updateVolume(); -	void updateBalance(); +	class SmackerVideoTrack : public FixedRateVideoTrack { +	public: +		SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature); +		~SmackerVideoTrack(); -	// FixedRateVideoDecoder API -	Common::Rational getFrameRate() const { return _frameRate; } +		bool isRewindable() const { return true; } +		bool rewind() { _curFrame = -1; return true; } -protected: -	void unpackPalette(); -	// Possible runs of blocks -	uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } -	void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum); +		uint16 getWidth() const; +		uint16 getHeight() const; +		Graphics::PixelFormat getPixelFormat() const; +		int getCurFrame() const { return _curFrame; } +		int getFrameCount() const { return _frameCount; } +		const Graphics::Surface *decodeNextFrame() { return _surface; } +		const byte *getPalette() const { _dirtyPalette = false; return _palette; } +		bool hasDirtyPalette() const { return _dirtyPalette; } + +		void readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize); +		void increaseCurFrame() { _curFrame++; } +		void decodeFrame(Common::BitStream &bs); +		void unpackPalette(Common::SeekableReadStream *stream); + +	protected: +		Common::Rational getFrameRate() const { return _frameRate; } + +		Graphics::Surface *_surface; + +	private: +		Common::Rational _frameRate; +		uint32 _flags, _signature; + +		byte _palette[3 * 256]; +		mutable bool _dirtyPalette; + +		int _curFrame; +		uint32 _frameCount; + +		BigHuffmanTree *_MMapTree; +		BigHuffmanTree *_MClrTree; +		BigHuffmanTree *_FullTree; +		BigHuffmanTree *_TypeTree; +		// Possible runs of blocks +		static uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } +	}; + +	virtual SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + +	Common::SeekableReadStream *_fileStream; + +private:  	enum AudioCompression {  		kCompressionNone,  		kCompressionDPCM, @@ -120,6 +151,25 @@ protected:  		uint32 dummy;  	} _header; +	class SmackerAudioTrack : public AudioTrack { +	public: +		SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType); +		~SmackerAudioTrack(); + +		Audio::Mixer::SoundType getSoundType() const { return _soundType; } + +		void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize); +		void queuePCM(byte *buffer, uint32 bufferSize); + +	protected: +		Audio::AudioStream *getAudioStream() const; + +	private: +		Audio::Mixer::SoundType _soundType; +		Audio::QueuingAudioStream *_audioStream; +		AudioInfo _audioInfo; +	}; +  	uint32 *_frameSizes;  	// The FrameTypes section of a Smacker file contains an array of bytes, where  	// the 8 bits of each byte describe the contents of the corresponding frame. @@ -127,25 +177,10 @@ protected:  	// and so on), so there can be up to 7 different audio tracks. When the lowest bit  	// (bit 0) is set, it denotes a frame that contains a palette record  	byte *_frameTypes; -	byte *_frameData; -	// The RGB palette -	byte _palette[3 * 256]; -	bool _dirtyPalette; -	Common::Rational _frameRate; -	uint32 _frameCount; -	Graphics::Surface *_surface; +	uint32 _firstFrameStart;  	Audio::Mixer::SoundType _soundType; -	Audio::Mixer *_mixer; -	bool _audioStarted; -	Audio::QueuingAudioStream *_audioStream; -	Audio::SoundHandle _audioHandle; - -	BigHuffmanTree *_MMapTree; -	BigHuffmanTree *_MClrTree; -	BigHuffmanTree *_FullTree; -	BigHuffmanTree *_TypeTree;  };  } // End of namespace Video | 
