diff options
Diffstat (limited to 'engines/titanic')
-rw-r--r-- | engines/titanic/module.mk | 1 | ||||
-rw-r--r-- | engines/titanic/support/avi_decoder.cpp | 949 | ||||
-rw-r--r-- | engines/titanic/support/avi_decoder.h | 285 | ||||
-rw-r--r-- | engines/titanic/support/mouse_cursor.cpp | 26 | ||||
-rw-r--r-- | engines/titanic/support/movie.cpp | 16 | ||||
-rw-r--r-- | engines/titanic/support/movie.h | 1 |
6 files changed, 36 insertions, 1242 deletions
diff --git a/engines/titanic/module.mk b/engines/titanic/module.mk index 7a142eabb0..be9a46784c 100644 --- a/engines/titanic/module.mk +++ b/engines/titanic/module.mk @@ -422,7 +422,6 @@ MODULE_OBJS := \ star_control/star_control_sub13.o \ star_control/star_control_sub14.o \ star_control/star_control_sub15.o \ - support/avi_decoder.o \ support/direct_draw.o \ support/direct_draw_surface.o \ support/files_manager.o \ diff --git a/engines/titanic/support/avi_decoder.cpp b/engines/titanic/support/avi_decoder.cpp deleted file mode 100644 index 578e3a94ea..0000000000 --- a/engines/titanic/support/avi_decoder.cpp +++ /dev/null @@ -1,949 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "common/stream.h" -#include "common/system.h" -#include "common/textconsole.h" - -#include "audio/audiostream.h" -#include "audio/mixer.h" - -#include "titanic/support/avi_decoder.h" - -// Audio Codecs -#include "audio/decoders/adpcm.h" -#include "audio/decoders/mp3.h" -#include "audio/decoders/raw.h" - -// Video Codecs -#include "image/codecs/codec.h" - -namespace Titanic { - -#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) - -// IDs used throughout the AVI files -// that will be handled by this player -#define ID_RIFF MKTAG('R','I','F','F') -#define ID_AVI MKTAG('A','V','I',' ') -#define ID_LIST MKTAG('L','I','S','T') -#define ID_HDRL MKTAG('h','d','r','l') -#define ID_AVIH MKTAG('a','v','i','h') -#define ID_STRL MKTAG('s','t','r','l') -#define ID_STRH MKTAG('s','t','r','h') -#define ID_VIDS MKTAG('v','i','d','s') -#define ID_AUDS MKTAG('a','u','d','s') -#define ID_MIDS MKTAG('m','i','d','s') -#define ID_TXTS MKTAG('t','x','t','s') -#define ID_JUNK MKTAG('J','U','N','K') -#define ID_JUNQ MKTAG('J','U','N','Q') -#define ID_DMLH MKTAG('d','m','l','h') -#define ID_STRF MKTAG('s','t','r','f') -#define ID_MOVI MKTAG('m','o','v','i') -#define ID_REC MKTAG('r','e','c',' ') -#define ID_VEDT MKTAG('v','e','d','t') -#define ID_IDX1 MKTAG('i','d','x','1') -#define ID_STRD MKTAG('s','t','r','d') -#define ID_INFO MKTAG('I','N','F','O') -#define ID_ISFT MKTAG('I','S','F','T') -#define ID_DISP MKTAG('D','I','S','P') -#define ID_PRMI MKTAG('P','R','M','I') -#define ID_STRN MKTAG('s','t','r','n') - -// Stream Types -enum { - kStreamTypePaletteChange = MKTAG16('p', 'c'), - kStreamTypeAudio = MKTAG16('w', 'b') -}; - - -AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType) : _frameRateOverride(0), _soundType(soundType) { - initCommon(); -} - -AVIDecoder::AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType) - : _frameRateOverride(frameRateOverride), _soundType(soundType) { - initCommon(); -} - -AVIDecoder::~AVIDecoder() { - close(); -} - -AVIDecoder::AVIAudioTrack *AVIDecoder::createAudioTrack(AVIStreamHeader sHeader, PCMWaveFormat wvInfo) { - return new AVIAudioTrack(sHeader, wvInfo, _soundType); -} - -void AVIDecoder::initCommon() { - _decodedHeader = false; - _foundMovieList = false; - _movieListStart = 0; - _movieListEnd = 0; - _fileStream = 0; - memset(&_header, 0, sizeof(_header)); -} - -bool AVIDecoder::isSeekable() const { - // Only videos with an index can seek - // Anyone else who wants to seek is crazy. - return isVideoLoaded() && !_indexEntries.empty(); -} - -bool AVIDecoder::parseNextChunk() { - uint32 tag = _fileStream->readUint32BE(); - uint32 size = _fileStream->readUint32LE(); - - if (_fileStream->eos()) - return false; - - debug(3, "Decoding tag %s", tag2str(tag)); - - switch (tag) { - case ID_LIST: - handleList(size); - break; - case ID_AVIH: - _header.size = size; - _header.microSecondsPerFrame = _fileStream->readUint32LE(); - _header.maxBytesPerSecond = _fileStream->readUint32LE(); - _header.padding = _fileStream->readUint32LE(); - _header.flags = _fileStream->readUint32LE(); - _header.totalFrames = _fileStream->readUint32LE(); - _header.initialFrames = _fileStream->readUint32LE(); - _header.streams = _fileStream->readUint32LE(); - _header.bufferSize = _fileStream->readUint32LE(); - _header.width = _fileStream->readUint32LE(); - _header.height = _fileStream->readUint32LE(); - // Ignore 16 bytes of reserved data - _fileStream->skip(16); - break; - case ID_STRH: - handleStreamHeader(size); - break; - case ID_STRD: // Extra stream info, safe to ignore - case ID_VEDT: // Unknown, safe to ignore - case ID_JUNK: // Alignment bytes, should be ignored - case ID_JUNQ: // Same as JUNK, safe to ignore - case ID_ISFT: // Metadata, safe to ignore - case ID_DISP: // Metadata, should be safe to ignore - case ID_STRN: // Metadata, safe to ignore - case ID_DMLH: // OpenDML extension, contains an extra total frames field, safe to ignore - skipChunk(size); - break; - case ID_IDX1: - readOldIndex(size); - break; - case 0: - return false; - default: - error("Unknown tag \'%s\' found", tag2str(tag)); - } - - return true; -} - -void AVIDecoder::skipChunk(uint32 size) { - // Make sure we're aligned on a word boundary - _fileStream->skip(size + (size & 1)); -} - -void AVIDecoder::handleList(uint32 listSize) { - uint32 listType = _fileStream->readUint32BE(); - listSize -= 4; // Subtract away listType's 4 bytes - uint32 curPos = _fileStream->pos(); - - debug(0, "Found LIST of type %s", tag2str(listType)); - - switch (listType) { - case ID_MOVI: // Movie List - // We found the movie block - _foundMovieList = true; - _movieListStart = curPos; - _movieListEnd = _movieListStart + listSize + (listSize & 1); - _fileStream->skip(listSize); - return; - case ID_HDRL: // Header List - // Mark the header as decoded - _decodedHeader = true; - break; - case ID_INFO: // Metadata - case ID_PRMI: // Adobe Premiere metadata, safe to ignore - // Ignore metadata - _fileStream->skip(listSize); - return; - case ID_STRL: // Stream list - default: // (Just hope we can parse it!) - break; - } - - while ((_fileStream->pos() - curPos) < listSize) - parseNextChunk(); -} - -void AVIDecoder::handleStreamHeader(uint32 size) { - AVIStreamHeader sHeader; - sHeader.size = size; - sHeader.streamType = _fileStream->readUint32BE(); - - if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) - error("Unhandled MIDI/Text stream"); - - sHeader.streamHandler = _fileStream->readUint32BE(); - sHeader.flags = _fileStream->readUint32LE(); - sHeader.priority = _fileStream->readUint16LE(); - sHeader.language = _fileStream->readUint16LE(); - sHeader.initialFrames = _fileStream->readUint32LE(); - sHeader.scale = _fileStream->readUint32LE(); - sHeader.rate = _fileStream->readUint32LE(); - sHeader.start = _fileStream->readUint32LE(); - sHeader.length = _fileStream->readUint32LE(); - sHeader.bufferSize = _fileStream->readUint32LE(); - sHeader.quality = _fileStream->readUint32LE(); - sHeader.sampleSize = _fileStream->readUint32LE(); - - _fileStream->skip(sHeader.size - 48); // Skip over the remainder of the chunk (frame) - - if (_fileStream->readUint32BE() != ID_STRF) - error("Could not find STRF tag"); - - uint32 strfSize = _fileStream->readUint32LE(); - uint32 startPos = _fileStream->pos(); - - if (sHeader.streamType == ID_VIDS) { - if (_frameRateOverride != 0) { - sHeader.rate = _frameRateOverride.getNumerator(); - sHeader.scale = _frameRateOverride.getDenominator(); - } - - BitmapInfoHeader bmInfo; - bmInfo.size = _fileStream->readUint32LE(); - bmInfo.width = _fileStream->readUint32LE(); - bmInfo.height = _fileStream->readUint32LE(); - bmInfo.planes = _fileStream->readUint16LE(); - bmInfo.bitCount = _fileStream->readUint16LE(); - bmInfo.compression = _fileStream->readUint32BE(); - bmInfo.sizeImage = _fileStream->readUint32LE(); - bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); - bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); - bmInfo.clrUsed = _fileStream->readUint32LE(); - bmInfo.clrImportant = _fileStream->readUint32LE(); - - if (bmInfo.clrUsed == 0) - bmInfo.clrUsed = 256; - - byte *initialPalette = 0; - - if (bmInfo.bitCount == 8) { - initialPalette = new byte[256 * 3]; - memset(initialPalette, 0, 256 * 3); - - byte *palette = initialPalette; - for (uint32 i = 0; i < bmInfo.clrUsed; i++) { - palette[i * 3 + 2] = _fileStream->readByte(); - palette[i * 3 + 1] = _fileStream->readByte(); - palette[i * 3] = _fileStream->readByte(); - _fileStream->readByte(); - } - } - - // WORKAROUND: For Titanic engine, the ycursors.avi file has two video tracks, - // so we do an explicit check below to ignore any second video track - if (getFrameCount() == 0) - addTrack(new AVIVideoTrack(_header.totalFrames, sHeader, bmInfo, initialPalette)); - } else if (sHeader.streamType == ID_AUDS) { - PCMWaveFormat wvInfo; - wvInfo.tag = _fileStream->readUint16LE(); - wvInfo.channels = _fileStream->readUint16LE(); - wvInfo.samplesPerSec = _fileStream->readUint32LE(); - wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); - wvInfo.blockAlign = _fileStream->readUint16LE(); - wvInfo.size = _fileStream->readUint16LE(); - - // AVI seems to treat the sampleSize as including the second - // channel as well, so divide for our sake. - if (wvInfo.channels == 2) - sHeader.sampleSize /= 2; - - AVIAudioTrack *track = createAudioTrack(sHeader, wvInfo); - track->createAudioStream(); - addTrack(track); - } - - // Ensure that we're at the end of the chunk - _fileStream->seek(startPos + strfSize); -} - -bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) { - close(); - - uint32 riffTag = stream->readUint32BE(); - if (riffTag != ID_RIFF) { - warning("Failed to find RIFF header"); - return false; - } - - /* uint32 fileSize = */ stream->readUint32LE(); - uint32 riffType = stream->readUint32BE(); - - if (riffType != ID_AVI) { - warning("RIFF not an AVI file"); - return false; - } - - _fileStream = stream; - - // Go through all chunks in the file - while (parseNextChunk()) - ; - - if (!_decodedHeader) { - warning("Failed to parse AVI header"); - close(); - return false; - } - - if (!_foundMovieList) { - warning("Failed to find 'MOVI' list"); - close(); - return false; - } - - // Create the status entries - uint32 index = 0; - for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++, index++) { - TrackStatus status; - status.track = *it; - status.index = index; - status.chunkSearchOffset = _movieListStart; - - if ((*it)->getTrackType() == Track::kTrackTypeVideo) { - if (_videoTracks.size() == 0) - _videoTracks.push_back(status); - } else { - if (_audioTracks.size() == 0) - _audioTracks.push_back(status); - } - } - - if (_videoTracks.size() != 1) { - warning("Unhandled AVI video track count: %d", _videoTracks.size()); - close(); - return false; - } - - // Check if this is a special Duck Truemotion video - checkTruemotion1(); - - return true; -} - -void AVIDecoder::close() { - VideoDecoder::close(); - - delete _fileStream; - _fileStream = 0; - _decodedHeader = false; - _foundMovieList = false; - _movieListStart = 0; - _movieListEnd = 0; - - _indexEntries.clear(); - memset(&_header, 0, sizeof(_header)); - - _videoTracks.clear(); - _audioTracks.clear(); -} - -void AVIDecoder::readNextPacket() { - // Shouldn't get this unless called on a non-open video - if (_videoTracks.empty()) - return; - - // Get the video frame first - handleNextPacket(_videoTracks[0]); - - // Handle audio tracks next - for (uint32 i = 0; i < _audioTracks.size(); i++) - handleNextPacket(_audioTracks[i]); -} - -void AVIDecoder::handleNextPacket(TrackStatus &status) { - // If there's no more to search, bail out - if (status.chunkSearchOffset + 8 >= _movieListEnd) { - if (status.track->getTrackType() == Track::kTrackTypeVideo) { - // Horrible AVI video has a premature end - // Force the frame to be the last frame - debug(0, "Forcing end of AVI video"); - ((AVIVideoTrack *)status.track)->forceTrackEnd(); - } - - return; - } - - // See if audio needs to be buffered and break out if not - if (status.track->getTrackType() == Track::kTrackTypeAudio && !shouldQueueAudio(status)) - return; - - // Seek to where we shall start searching - _fileStream->seek(status.chunkSearchOffset); - - for (;;) { - // If there's no more to search, bail out - if ((uint32)_fileStream->pos() + 8 >= _movieListEnd) { - if (status.track->getTrackType() == Track::kTrackTypeVideo) { - // Horrible AVI video has a premature end - // Force the frame to be the last frame - debug(0, "Forcing end of AVI video"); - ((AVIVideoTrack *)status.track)->forceTrackEnd(); - } - - break; - } - - uint32 nextTag = _fileStream->readUint32BE(); - uint32 size = _fileStream->readUint32LE(); - - if (nextTag == ID_LIST) { - // A list of audio/video chunks - if (_fileStream->readUint32BE() != ID_REC) - error("Expected 'rec ' LIST"); - - continue; - } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { - skipChunk(size); - continue; - } - - // Only accept chunks for this stream - uint32 streamIndex = getStreamIndex(nextTag); - if (streamIndex != status.index) { - skipChunk(size); - continue; - } - - Common::SeekableReadStream *chunk = 0; - - if (size != 0) { - chunk = _fileStream->readStream(size); - _fileStream->skip(size & 1); - } - - if (status.track->getTrackType() == Track::kTrackTypeAudio) { - if (getStreamType(nextTag) != kStreamTypeAudio) - error("Invalid audio track tag '%s'", tag2str(nextTag)); - - assert(chunk); - ((AVIAudioTrack *)status.track)->queueSound(chunk); - - // Break out if we have enough audio - if (!shouldQueueAudio(status)) - break; - } else { - AVIVideoTrack *videoTrack = (AVIVideoTrack *)status.track; - - if (getStreamType(nextTag) == kStreamTypePaletteChange) { - // Palette Change - videoTrack->loadPaletteFromChunk(chunk); - } else { - // Otherwise, assume it's a compressed frame - videoTrack->decodeFrame(chunk); - break; - } - } - } - - // Start us off in this position next time - status.chunkSearchOffset = _fileStream->pos(); -} - -bool AVIDecoder::shouldQueueAudio(TrackStatus& status) { - // Sanity check: - if (status.track->getTrackType() != Track::kTrackTypeAudio) - return false; - - // If video is done, make sure that the rest of the audio is queued - // (I guess this is also really a sanity check) - AVIVideoTrack *videoTrack = (AVIVideoTrack *)_videoTracks[0].track; - if (videoTrack->endOfTrack()) - return true; - - // Being three frames ahead should be enough for any video. - return ((AVIAudioTrack *)status.track)->getCurChunk() < (uint32)(videoTrack->getCurFrame() + 3); -} - -bool AVIDecoder::rewind() { - if (!VideoDecoder::rewind()) - return false; - - for (uint32 i = 0; i < _videoTracks.size(); i++) - _videoTracks[i].chunkSearchOffset = _movieListStart; - - for (uint32 i = 0; i < _audioTracks.size(); i++) - _audioTracks[i].chunkSearchOffset = _movieListStart; - - return true; -} - -bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { - // Can't seek beyond the end - if (time > getDuration()) - return false; - - // Get our video - AVIVideoTrack *videoTrack = (AVIVideoTrack *)_videoTracks[0].track; - uint32 videoIndex = _videoTracks[0].index; - - // If we seek directly to the end, just mark the tracks as over - if (time == getDuration()) { - videoTrack->setCurFrame(videoTrack->getFrameCount() - 1); - - for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) - if ((*it)->getTrackType() == Track::kTrackTypeAudio) - ((AVIAudioTrack *)*it)->resetStream(); - - return true; - } - - // Get the frame we should be on at this time - uint frame = videoTrack->getFrameAtTime(time); - - // Reset any palette, if necessary - videoTrack->useInitialPalette(); - - int lastKeyFrame = -1; - int frameIndex = -1; - uint curFrame = 0; - - // Go through and figure out where we should be - // If there's a palette, we need to find the palette too - for (uint32 i = 0; i < _indexEntries.size(); i++) { - const OldIndex &index = _indexEntries[i]; - - // We don't care about RECs - if (index.id == ID_REC) - continue; - - // We're only looking at entries for this track - if (getStreamIndex(index.id) != videoIndex) - continue; - - uint16 streamType = getStreamType(index.id); - - if (streamType == kStreamTypePaletteChange) { - // We need to handle any palette change we see since there's no - // flag to tell if this is a "key" palette. - // Decode the palette - _fileStream->seek(_indexEntries[i].offset + 8); - Common::SeekableReadStream *chunk = 0; - - if (_indexEntries[i].size != 0) - chunk = _fileStream->readStream(_indexEntries[i].size); - - videoTrack->loadPaletteFromChunk(chunk); - } else { - // Check to see if this is a keyframe - // The first frame has to be a keyframe - if ((_indexEntries[i].flags & AVIIF_INDEX) || curFrame == 0) - lastKeyFrame = i; - - // Did we find the target frame? - if (frame == curFrame) { - frameIndex = i; - break; - } - - curFrame++; - } - } - - if (frameIndex < 0) // This shouldn't happen. - return false; - - // Update all the audio tracks - for (uint32 i = 0; i < _audioTracks.size(); i++) { - AVIAudioTrack *audioTrack = (AVIAudioTrack *)_audioTracks[i].track; - - // Recreate the audio stream - audioTrack->resetStream(); - - // Set the chunk index for the track - audioTrack->setCurChunk(frame); - - uint32 chunksFound = 0; - for (uint32 j = 0; j < _indexEntries.size(); j++) { - const OldIndex &index = _indexEntries[j]; - - // Continue ignoring RECs - if (index.id == ID_REC) - continue; - - if (getStreamIndex(index.id) == _audioTracks[i].index) { - if (chunksFound == frame) { - _fileStream->seek(index.offset + 8); - Common::SeekableReadStream *audioChunk = _fileStream->readStream(index.size); - audioTrack->queueSound(audioChunk); - _audioTracks[i].chunkSearchOffset = (j == _indexEntries.size() - 1) ? _movieListEnd : _indexEntries[j + 1].offset; - break; - } - - chunksFound++; - } - } - - // Skip any audio to bring us to the right time - audioTrack->skipAudio(time, videoTrack->getFrameTime(frame)); - } - - // Decode from keyFrame to curFrame - 1 - for (int i = lastKeyFrame; i < frameIndex; i++) { - if (_indexEntries[i].id == ID_REC) - continue; - - if (getStreamIndex(_indexEntries[i].id) != videoIndex) - continue; - - uint16 streamType = getStreamType(_indexEntries[i].id); - - // Ignore palettes, they were already handled - if (streamType == kStreamTypePaletteChange) - continue; - - // Frame, hopefully - _fileStream->seek(_indexEntries[i].offset + 8); - Common::SeekableReadStream *chunk = 0; - - if (_indexEntries[i].size != 0) - chunk = _fileStream->readStream(_indexEntries[i].size); - - videoTrack->decodeFrame(chunk); - } - - // Set the video track's frame - videoTrack->setCurFrame((int)frame - 1); - - // Set the video track's search offset to the right spot - _videoTracks[0].chunkSearchOffset = _indexEntries[frameIndex].offset; - return true; -} - -byte AVIDecoder::getStreamIndex(uint32 tag) const { - char string[3]; - WRITE_BE_UINT16(string, tag >> 16); - string[2] = 0; - return strtol(string, 0, 16); -} - -void AVIDecoder::readOldIndex(uint32 size) { - uint32 entryCount = size / 16; - - debug(0, "Old Index: %d entries", entryCount); - - if (entryCount == 0) - return; - - // Read the first index separately - OldIndex firstEntry; - firstEntry.id = _fileStream->readUint32BE(); - firstEntry.flags = _fileStream->readUint32LE(); - firstEntry.offset = _fileStream->readUint32LE(); - firstEntry.size = _fileStream->readUint32LE(); - - // Check if the offset is already absolute - // If it's absolute, the offset will equal the start of the movie list - bool isAbsolute = firstEntry.offset == _movieListStart; - - debug(1, "Old index is %s", isAbsolute ? "absolute" : "relative"); - - if (!isAbsolute) - firstEntry.offset += _movieListStart - 4; - - debug(0, "Index 0: Tag '%s', Offset = %d, Size = %d (Flags = %d)", tag2str(firstEntry.id), firstEntry.offset, firstEntry.size, firstEntry.flags); - _indexEntries.push_back(firstEntry); - - for (uint32 i = 1; i < entryCount; i++) { - OldIndex indexEntry; - indexEntry.id = _fileStream->readUint32BE(); - indexEntry.flags = _fileStream->readUint32LE(); - indexEntry.offset = _fileStream->readUint32LE(); - indexEntry.size = _fileStream->readUint32LE(); - - // Adjust to absolute, if necessary - if (!isAbsolute) - indexEntry.offset += _movieListStart - 4; - - _indexEntries.push_back(indexEntry); - debug(0, "Index %d: Tag '%s', Offset = %d, Size = %d (Flags = %d)", i, tag2str(indexEntry.id), indexEntry.offset, indexEntry.size, indexEntry.flags); - } -} - -void AVIDecoder::checkTruemotion1() { - // If we got here from loadStream(), we know the track is valid - assert(!_videoTracks.empty()); - - TrackStatus &status = _videoTracks[0]; - AVIVideoTrack *track = (AVIVideoTrack *)status.track; - - // Ignore non-truemotion tracks - if (!track->isTruemotion1()) - return; - - // Read the next video packet - handleNextPacket(status); - - const Graphics::Surface *frame = track->decodeNextFrame(); - if (!frame) { - rewind(); - return; - } - - // Fill in the width/height based on the frame's width/height - _header.width = frame->w; - _header.height = frame->h; - track->forceDimensions(frame->w, frame->h); - - // Rewind us back to the beginning - rewind(); -} - -Video::VideoDecoder::AudioTrack *AVIDecoder::getAudioTrack(int index) { - // AVI audio track indexes are relative to the first track - Track *track = getTrack(index); - - if (!track || track->getTrackType() != Track::kTrackTypeAudio) - return 0; - - return (AudioTrack *)track; -} - -AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader, byte *initialPalette) - : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader), _initialPalette(initialPalette) { - _videoCodec = createCodec(); - _lastFrame = 0; - _curFrame = -1; - - useInitialPalette(); -} - -AVIDecoder::AVIVideoTrack::~AVIVideoTrack() { - delete _videoCodec; - delete[] _initialPalette; -} - -void AVIDecoder::AVIVideoTrack::decodeFrame(Common::SeekableReadStream *stream) { - if (stream) { - if (_videoCodec) - _lastFrame = _videoCodec->decodeFrame(*stream); - } else { - // Empty frame - _lastFrame = 0; - } - - delete stream; - _curFrame++; -} - -Graphics::PixelFormat AVIDecoder::AVIVideoTrack::getPixelFormat() const { - if (_videoCodec) - return _videoCodec->getPixelFormat(); - - return Graphics::PixelFormat(); -} - -void AVIDecoder::AVIVideoTrack::loadPaletteFromChunk(Common::SeekableReadStream *chunk) { - assert(chunk); - byte firstEntry = chunk->readByte(); - uint16 numEntries = chunk->readByte(); - chunk->readUint16LE(); // Reserved - - // 0 entries means all colors are going to be changed - if (numEntries == 0) - numEntries = 256; - - for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { - _palette[i * 3] = chunk->readByte(); - _palette[i * 3 + 1] = chunk->readByte(); - _palette[i * 3 + 2] = chunk->readByte(); - chunk->readByte(); // Flags that don't serve us any purpose - } - - delete chunk; - _dirtyPalette = true; -} - -void AVIDecoder::AVIVideoTrack::useInitialPalette() { - _dirtyPalette = false; - - if (_initialPalette) { - memcpy(_palette, _initialPalette, sizeof(_palette)); - _dirtyPalette = true; - } -} - -bool AVIDecoder::AVIVideoTrack::isTruemotion1() const { - return _bmInfo.compression == MKTAG('D', 'U', 'C', 'K') || _bmInfo.compression == MKTAG('d', 'u', 'c', 'k'); -} - -void AVIDecoder::AVIVideoTrack::forceDimensions(uint16 width, uint16 height) { - _bmInfo.width = width; - _bmInfo.height = height; -} - -bool AVIDecoder::AVIVideoTrack::rewind() { - _curFrame = -1; - - useInitialPalette(); - - delete _videoCodec; - _videoCodec = createCodec(); - _lastFrame = 0; - return true; -} - -Image::Codec *AVIDecoder::AVIVideoTrack::createCodec() { - return Image::createBitmapCodec(_bmInfo.compression, _bmInfo.width, _bmInfo.height, _bmInfo.bitCount); -} - -void AVIDecoder::AVIVideoTrack::forceTrackEnd() { - _curFrame = _frameCount - 1; -} - -const byte *AVIDecoder::AVIVideoTrack::getPalette() const { - if (_videoCodec && _videoCodec->containsPalette()) - return _videoCodec->getPalette(); - - _dirtyPalette = false; - return _palette; -} - -bool AVIDecoder::AVIVideoTrack::hasDirtyPalette() const { - if (_videoCodec && _videoCodec->containsPalette()) - return _videoCodec->hasDirtyPalette(); - - return _dirtyPalette; -} - -bool AVIDecoder::AVIVideoTrack::canDither() const { - return _videoCodec && _videoCodec->canDither(Image::Codec::kDitherTypeVFW); -} - -void AVIDecoder::AVIVideoTrack::setDither(const byte *palette) { - assert(_videoCodec); - _videoCodec->setDither(Image::Codec::kDitherTypeVFW, palette); -} - -AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) - : _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType), _audioStream(0), _packetStream(0), _curChunk(0) { -} - -AVIDecoder::AVIAudioTrack::~AVIAudioTrack() { - delete _audioStream; -} - -void AVIDecoder::AVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { - if (_packetStream) - _packetStream->queuePacket(stream); - else - delete stream; - - _curChunk++; -} - -void AVIDecoder::AVIAudioTrack::skipAudio(const Audio::Timestamp &time, const Audio::Timestamp &frameTime) { - Audio::Timestamp timeDiff = time.convertToFramerate(_wvInfo.samplesPerSec) - frameTime.convertToFramerate(_wvInfo.samplesPerSec); - int skipFrames = timeDiff.totalNumberOfFrames(); - - if (skipFrames <= 0) - return; - - Audio::AudioStream *audioStream = getAudioStream(); - if (!audioStream) - return; - - if (audioStream->isStereo()) - skipFrames *= 2; - - int16 *tempBuffer = new int16[skipFrames]; - audioStream->readBuffer(tempBuffer, skipFrames); - delete[] tempBuffer; -} - -void AVIDecoder::AVIAudioTrack::resetStream() { - delete _audioStream; - createAudioStream(); - _curChunk = 0; -} - -bool AVIDecoder::AVIAudioTrack::rewind() { - resetStream(); - return true; -} - -void AVIDecoder::AVIAudioTrack::createAudioStream() { - _packetStream = 0; - - switch (_wvInfo.tag) { - case kWaveFormatPCM: { - byte flags = 0; - if (_audsHeader.sampleSize == 2) - flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; - else - flags |= Audio::FLAG_UNSIGNED; - - if (_wvInfo.channels == 2) - flags |= Audio::FLAG_STEREO; - - _packetStream = Audio::makePacketizedRawStream(_wvInfo.samplesPerSec, flags); - break; - } - case kWaveFormatMSADPCM: - _packetStream = Audio::makePacketizedADPCMStream(Audio::kADPCMMS, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign); - break; - case kWaveFormatMSIMAADPCM: - _packetStream = Audio::makePacketizedADPCMStream(Audio::kADPCMMSIma, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign); - break; - case kWaveFormatDK3: - _packetStream = Audio::makePacketizedADPCMStream(Audio::kADPCMDK3, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign); - break; - case kWaveFormatMP3: -#ifdef USE_MAD - _packetStream = Audio::makePacketizedMP3Stream(_wvInfo.channels, _wvInfo.samplesPerSec); -#else - warning("AVI MP3 stream found, but no libmad support compiled in"); -#endif - break; - case kWaveFormatNone: - break; - default: - warning("Unsupported AVI audio format %d", _wvInfo.tag); - break; - } - - if (_packetStream) - _audioStream = _packetStream; - else - _audioStream = Audio::makeNullAudioStream(); -} - -AVIDecoder::TrackStatus::TrackStatus() : track(0), chunkSearchOffset(0) { -} - -} // End of namespace Titanic diff --git a/engines/titanic/support/avi_decoder.h b/engines/titanic/support/avi_decoder.h deleted file mode 100644 index acc33cbc4d..0000000000 --- a/engines/titanic/support/avi_decoder.h +++ /dev/null @@ -1,285 +0,0 @@ -/* 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 TITANIC_AVI_DECODER_H -#define TITANIC_AVI_DECODER_H - -#include "common/array.h" -#include "common/rational.h" -#include "common/rect.h" -#include "common/str.h" - -#include "video/video_decoder.h" -#include "audio/mixer.h" - -namespace Audio { -class AudioStream; -class PacketizedAudioStream; -} - -namespace Common { -class SeekableReadStream; -} - -namespace Graphics { -struct PixelFormat; -} - -namespace Image { -class Codec; -} - -namespace Titanic { - -/** - * Modified AVI Decoder used by Titanic engine. - */ -class AVIDecoder : public Video::VideoDecoder { -public: - AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); - AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); - virtual ~AVIDecoder(); - - bool loadStream(Common::SeekableReadStream *stream); - void close(); - uint16 getWidth() const { return _header.width; } - uint16 getHeight() const { return _header.height; } - - bool rewind(); - bool isRewindable() const { return true; } - bool isSeekable() const; - -protected: - // VideoDecoder API - void readNextPacket(); - bool seekIntern(const Audio::Timestamp &time); - bool supportsAudioTrackSwitching() const { return true; } - AudioTrack *getAudioTrack(int index); - - struct BitmapInfoHeader { - uint32 size; - uint32 width; - uint32 height; - uint16 planes; - uint16 bitCount; - uint32 compression; - uint32 sizeImage; - uint32 xPelsPerMeter; - uint32 yPelsPerMeter; - uint32 clrUsed; - uint32 clrImportant; - }; - - struct WaveFormat { - uint16 tag; - uint16 channels; - uint32 samplesPerSec; - uint32 avgBytesPerSec; - uint16 blockAlign; - }; - - struct PCMWaveFormat : public WaveFormat { - uint16 size; - }; - - struct WaveFormatEX : public WaveFormat { - uint16 bitsPerSample; - uint16 size; - }; - - struct OldIndex { - uint32 id; - uint32 flags; - uint32 offset; - uint32 size; - }; - - // Index Flags - enum IndexFlags { - AVIIF_INDEX = 0x10 - }; - - struct AVIHeader { - uint32 size; - uint32 microSecondsPerFrame; - uint32 maxBytesPerSecond; - uint32 padding; - uint32 flags; - uint32 totalFrames; - uint32 initialFrames; - uint32 streams; - uint32 bufferSize; - uint32 width; - uint32 height; - }; - - // Flags from the AVIHeader - enum AVIFlags { - AVIF_HASINDEX = 0x00000010, - AVIF_MUSTUSEINDEX = 0x00000020, - AVIF_ISINTERLEAVED = 0x00000100, - AVIF_TRUSTCKTYPE = 0x00000800, - AVIF_WASCAPTUREFILE = 0x00010000, - AVIF_WASCOPYRIGHTED = 0x00020000 - }; - - struct AVIStreamHeader { - uint32 size; - uint32 streamType; - uint32 streamHandler; - uint32 flags; - uint16 priority; - uint16 language; - uint32 initialFrames; - uint32 scale; - uint32 rate; - uint32 start; - uint32 length; - uint32 bufferSize; - uint32 quality; - uint32 sampleSize; - Common::Rect frame; - }; - - class AVIVideoTrack : public FixedRateVideoTrack { - public: - AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader, byte *initialPalette = 0); - ~AVIVideoTrack(); - - void decodeFrame(Common::SeekableReadStream *stream); - void forceTrackEnd(); - - uint16 getWidth() const { return _bmInfo.width; } - uint16 getHeight() const { return _bmInfo.height; } - Graphics::PixelFormat getPixelFormat() const; - int getCurFrame() const { return _curFrame; } - int getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame() { return _lastFrame; } - - const byte *getPalette() const; - bool hasDirtyPalette() const; - void setCurFrame(int frame) { _curFrame = frame; } - void loadPaletteFromChunk(Common::SeekableReadStream *chunk); - void useInitialPalette(); - bool canDither() const; - void setDither(const byte *palette); - - bool isTruemotion1() const; - void forceDimensions(uint16 width, uint16 height); - - bool isRewindable() const { return true; } - bool rewind(); - - protected: - Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } - - private: - AVIStreamHeader _vidsHeader; - BitmapInfoHeader _bmInfo; - byte _palette[3 * 256]; - byte *_initialPalette; - mutable bool _dirtyPalette; - int _frameCount, _curFrame; - - Image::Codec *_videoCodec; - const Graphics::Surface *_lastFrame; - Image::Codec *createCodec(); - }; - - class AVIAudioTrack : public AudioTrack { - public: - AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType); - ~AVIAudioTrack(); - - virtual void createAudioStream(); - virtual void queueSound(Common::SeekableReadStream *stream); - Audio::Mixer::SoundType getSoundType() const { return _soundType; } - void skipAudio(const Audio::Timestamp &time, const Audio::Timestamp &frameTime); - virtual void resetStream(); - uint32 getCurChunk() const { return _curChunk; } - void setCurChunk(uint32 chunk) { _curChunk = chunk; } - - bool isRewindable() const { return true; } - bool rewind(); - - protected: - Audio::AudioStream *getAudioStream() const { return _audioStream; } - - // Audio Codecs - enum { - kWaveFormatNone = 0, - kWaveFormatPCM = 1, - kWaveFormatMSADPCM = 2, - kWaveFormatMSIMAADPCM = 17, - kWaveFormatMP3 = 85, - kWaveFormatDK3 = 98 // rogue format number - }; - - AVIStreamHeader _audsHeader; - PCMWaveFormat _wvInfo; - Audio::Mixer::SoundType _soundType; - Audio::AudioStream *_audioStream; - Audio::PacketizedAudioStream *_packetStream; - uint32 _curChunk; - }; - - struct TrackStatus { - TrackStatus(); - - Track *track; - uint32 index; - uint32 chunkSearchOffset; - }; - - AVIHeader _header; - - void readOldIndex(uint32 size); - Common::Array<OldIndex> _indexEntries; - - Common::SeekableReadStream *_fileStream; - bool _decodedHeader; - bool _foundMovieList; - uint32 _movieListStart, _movieListEnd; - - Audio::Mixer::SoundType _soundType; - Common::Rational _frameRateOverride; - void initCommon(); - - bool parseNextChunk(); - void skipChunk(uint32 size); - void handleList(uint32 listSize); - void handleStreamHeader(uint32 size); - uint16 getStreamType(uint32 tag) const { return tag & 0xFFFF; } - byte getStreamIndex(uint32 tag) const; - void checkTruemotion1(); - - void handleNextPacket(TrackStatus& status); - bool shouldQueueAudio(TrackStatus& status); - Common::Array<TrackStatus> _videoTracks, _audioTracks; - -public: - virtual AVIAudioTrack *createAudioTrack(AVIStreamHeader sHeader, PCMWaveFormat wvInfo); -}; - -} // End of namespace Titanic - -#endif diff --git a/engines/titanic/support/mouse_cursor.cpp b/engines/titanic/support/mouse_cursor.cpp index a2bd11657c..6ddfecfd2a 100644 --- a/engines/titanic/support/mouse_cursor.cpp +++ b/engines/titanic/support/mouse_cursor.cpp @@ -20,8 +20,9 @@ * */ -#include "graphics/cursorman.h" +#include "common/memstream.h" #include "common/textconsole.h" +#include "graphics/cursorman.h" #include "titanic/support/mouse_cursor.h" #include "titanic/support/movie.h" #include "titanic/support/screen_manager.h" @@ -62,20 +63,39 @@ CMouseCursor::~CMouseCursor() { void CMouseCursor::loadCursorImages() { const CString name("ycursors.avi"); - const CResourceKey key(name); g_vm->_filesManager.fn4(name); + // WORKAROUND: We need to manipulate ycursors.avi file so it can be read + // by the ScummVM AVIDecoder, by removing the redundant second video track + Common::File f; + if (!f.open(name)) + error("Could not open cursors file"); + + // Read in the entire file + byte *movieData = (byte *)malloc(f.size()); + f.read(movieData, f.size()); + + if (READ_BE_UINT32(movieData + 254) == MKTAG('s', 't', 'r', 'h')) { + // Change the second video chunk to junk data so it gets ignored + WRITE_BE_UINT32(movieData + 254, MKTAG('J', 'U', 'N', 'K')); + WRITE_LE_UINT32(movieData + 258, 1128); + } + // Iterate through each cursor for (int idx = 0; idx < NUM_CURSORS; ++idx) { assert(CURSOR_DATA[idx][0] == (idx + 1)); _cursors[idx]._centroid = Common::Point(CURSOR_DATA[idx][2], CURSOR_DATA[idx][3]); + // Create the surface CVideoSurface *surface = _screenManager->createSurface(64, 64); _cursors[idx]._videoSurface = surface; - OSMovie movie(key, surface); + Common::SeekableReadStream *stream = new Common::MemoryReadStream( + movieData, f.size(), DisposeAfterUse::NO); + OSMovie movie(stream, surface); movie.setFrame(idx); + _cursors[idx]._ptrUnknown = movie.proc21(); surface->set40(_cursors[idx]._ptrUnknown); } diff --git a/engines/titanic/support/movie.cpp b/engines/titanic/support/movie.cpp index 7593c74e42..6599093226 100644 --- a/engines/titanic/support/movie.cpp +++ b/engines/titanic/support/movie.cpp @@ -20,8 +20,7 @@ * */ -#include "image/codecs/cinepak.h" -#include "titanic/support/avi_decoder.h" +#include "video/avi_decoder.h" #include "titanic/support/movie.h" #include "titanic/titanic.h" @@ -51,11 +50,18 @@ bool CMovie::get10() { OSMovie::OSMovie(const CResourceKey &name, CVideoSurface *surface) : _videoSurface(surface), _gameObject(nullptr) { - _video = new AVIDecoder(); + _video = new Video::AVIDecoder(); if (!_video->loadFile(name.getString())) error("Could not open video - %s", name.getString().c_str()); } +OSMovie::OSMovie(Common::SeekableReadStream *stream, CVideoSurface *surface) : + _videoSurface(surface), _gameObject(nullptr) { + _video = new Video::AVIDecoder(); + if (!_video->loadStream(stream)) + error("Could not parse movie stream"); +} + OSMovie::~OSMovie() { g_vm->_activeMovies.remove(this); delete _video; @@ -134,7 +140,9 @@ MovieState OSMovie::getState() { void OSMovie::update() { if (_state != MOVIE_STOPPED) { if (_video->isPlaying()) { - if (_video->needsUpdate()) { + if (_video->endOfVideo()) { + _state = MOVIE_FINISHED; + } else if (_video->needsUpdate()) { decodeFrame(); _state = MOVIE_FRAME; } else { diff --git a/engines/titanic/support/movie.h b/engines/titanic/support/movie.h index b488d26e39..2751f2d814 100644 --- a/engines/titanic/support/movie.h +++ b/engines/titanic/support/movie.h @@ -93,6 +93,7 @@ public: CGameObject *_gameObject; public: OSMovie(const CResourceKey &name, CVideoSurface *surface); + OSMovie(Common::SeekableReadStream *stream, CVideoSurface *surface); virtual ~OSMovie(); /** |