aboutsummaryrefslogtreecommitdiff
path: root/engines/titanic
diff options
context:
space:
mode:
Diffstat (limited to 'engines/titanic')
-rw-r--r--engines/titanic/module.mk1
-rw-r--r--engines/titanic/support/avi_decoder.cpp949
-rw-r--r--engines/titanic/support/avi_decoder.h285
-rw-r--r--engines/titanic/support/mouse_cursor.cpp26
-rw-r--r--engines/titanic/support/movie.cpp16
-rw-r--r--engines/titanic/support/movie.h1
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();
/**