From 80a75d723602cc6c1946d5b0605cdb34e58fa2ab Mon Sep 17 00:00:00 2001 From: elasota Date: Tue, 13 Jun 2023 22:37:30 -0400 Subject: [PATCH] MTROPOLIS: Add AVI movie support --- engines/mtropolis/asset_factory.cpp | 2 + engines/mtropolis/assets.cpp | 15 +++++ engines/mtropolis/assets.h | 11 ++++ engines/mtropolis/data.cpp | 36 ++++++++++- engines/mtropolis/data.h | 21 +++++- engines/mtropolis/element_factory.cpp | 1 + engines/mtropolis/elements.cpp | 92 ++++++++++++++++++--------- engines/mtropolis/runtime.h | 1 + 8 files changed, 144 insertions(+), 35 deletions(-) diff --git a/engines/mtropolis/asset_factory.cpp b/engines/mtropolis/asset_factory.cpp index 7b0071bb410..ac61e23da58 100644 --- a/engines/mtropolis/asset_factory.cpp +++ b/engines/mtropolis/asset_factory.cpp @@ -65,6 +65,8 @@ SIAssetFactory *getAssetFactoryForDataObjectType(const Data::DataObjectTypes::Da return AssetFactory::getInstance(); case Data::DataObjectTypes::kMovieAsset: return AssetFactory::getInstance(); + case Data::DataObjectTypes::kAVIMovieAsset: + return AssetFactory::getInstance(); case Data::DataObjectTypes::kImageAsset: return AssetFactory::getInstance(); case Data::DataObjectTypes::kMToonAsset: diff --git a/engines/mtropolis/assets.cpp b/engines/mtropolis/assets.cpp index 896c57bd2c1..eb0e8bf3d57 100644 --- a/engines/mtropolis/assets.cpp +++ b/engines/mtropolis/assets.cpp @@ -802,6 +802,21 @@ void MovieAsset::addDamagedFrame(int frame) { _damagedFrames.push_back(frame); } +bool AVIMovieAsset::load(AssetLoaderContext &context, const Data::AVIMovieAsset &data) { + _assetID = data.assetID; + _extFileName = data.extFileName; + + return true; +} + +AssetType AVIMovieAsset::getAssetType() const { + return kAssetTypeAVIMovie; +} + +const Common::String &AVIMovieAsset::getExtFileName() const { + return _extFileName; +} + const Common::Array &MovieAsset::getDamagedFrames() const { return _damagedFrames; } diff --git a/engines/mtropolis/assets.h b/engines/mtropolis/assets.h index a18b0f0f27d..f440199c2f2 100644 --- a/engines/mtropolis/assets.h +++ b/engines/mtropolis/assets.h @@ -230,6 +230,17 @@ private: Common::Array _damagedFrames; }; +class AVIMovieAsset : public Asset { +public: + bool load(AssetLoaderContext &context, const Data::AVIMovieAsset &data); + AssetType getAssetType() const override; + + const Common::String &getExtFileName() const; + +private: + Common::String _extFileName; +}; + class CachedImage { public: CachedImage(); diff --git a/engines/mtropolis/data.cpp b/engines/mtropolis/data.cpp index 64bf22ccda5..72db1a11257 100644 --- a/engines/mtropolis/data.cpp +++ b/engines/mtropolis/data.cpp @@ -33,6 +33,7 @@ namespace DataObjectTypes { bool isValidSceneRootElement(DataObjectType type) { switch (type) { case kGraphicElement: + case kAVIMovieElement: case kMovieElement: case kMToonElement: case kImageElement: @@ -45,6 +46,7 @@ bool isValidSceneRootElement(DataObjectType type) { bool isVisualElement(DataObjectType type) { switch (type) { case kGraphicElement: + case kAVIMovieElement: case kMovieElement: case kMToonElement: case kImageElement: @@ -67,6 +69,7 @@ bool isNonVisualElement(DataObjectType type) { bool isElement(DataObjectType type) { switch (type) { case kGraphicElement: + case kAVIMovieElement: case kMovieElement: case kMToonElement: case kImageElement: @@ -140,6 +143,7 @@ bool isModifier(DataObjectType type) { bool isAsset(DataObjectType type) { switch (type) { case kMovieAsset: + case kAVIMovieAsset: case kAudioAsset: case kColorTableAsset: case kImageAsset: @@ -950,11 +954,11 @@ DataReadErrorCode SoundElement::load(DataReader& reader) { return kDataReadErrorNone; } -MovieElement::MovieElement() +MovieElement::MovieElement(bool avi) : sizeIncludingTag(0), guid(0), lengthOfName(0), elementFlags(0), layer(0), unknown3{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, sectionID(0), unknown5{0, 0}, assetID(0), unknown7(0), volume(0), animationFlags(0), - unknown10{0, 0, 0, 0}, unknown11{0, 0, 0, 0}, streamLocator(0), unknown13{0, 0, 0, 0} { + unknown10{0, 0, 0, 0}, unknown11{0, 0, 0, 0}, streamLocator(0), unknown13{0, 0, 0, 0}, isAVI(avi) { } DataReadErrorCode MovieElement::load(DataReader &reader) { @@ -2078,6 +2082,25 @@ DataReadErrorCode MovieAsset::load(DataReader &reader) { return kDataReadErrorNone; } +AVIMovieAsset::AVIMovieAsset() : unknown1{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, assetID(0), unknown2{0, 0, 0, 0}, + extFileNameLength(0) { + for (uint8 &v : unknown3) + v = 0; +} + +DataReadErrorCode AVIMovieAsset::load(DataReader &reader) { + if (_revision != 0) + return kDataReadErrorUnsupportedRevision; + + if (!reader.readBytes(unknown1) || !reader.readU32(assetID) || !reader.readBytes(unknown2) || !reader.readU16(extFileNameLength) || !reader.readBytes(unknown3)) + return kDataReadErrorReadFailed; + + if (!reader.readTerminatedStr(extFileName, extFileNameLength)) + return kDataReadErrorReadFailed; + + return kDataReadErrorNone; +} + AudioAsset::AudioAsset() : persistFlags(0), assetAndDataCombinedSize(0), unknown2{0, 0, 0, 0}, assetID(0), unknown3{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, sampleRate1(0), bitsPerSample(0), @@ -2421,7 +2444,10 @@ DataReadErrorCode loadDataObject(const PlugInModifierRegistry ®istry, DataRea dataObject = new GraphicElement(); break; case DataObjectTypes::kMovieElement: - dataObject = new MovieElement(); + dataObject = new MovieElement(false); + break; + case DataObjectTypes::kAVIMovieElement: + dataObject = new MovieElement(true); break; case DataObjectTypes::kMToonElement: dataObject = new MToonElement(); @@ -2561,6 +2587,10 @@ DataReadErrorCode loadDataObject(const PlugInModifierRegistry ®istry, DataRea dataObject = new ColorTableAsset(); break; + case DataObjectTypes::kAVIMovieAsset: + dataObject = new AVIMovieAsset(); + break; + case DataObjectTypes::kMovieAsset: dataObject = new MovieAsset(); break; diff --git a/engines/mtropolis/data.h b/engines/mtropolis/data.h index 05a0431607b..a678bdb3649 100644 --- a/engines/mtropolis/data.h +++ b/engines/mtropolis/data.h @@ -100,6 +100,7 @@ enum DataObjectType : uint { kSoundElement = 0xa, kTextLabelElement = 0x15, + kAVIMovieElement = 0x25, kAliasModifier = 0x27, kChangeSceneModifier = 0x136, kReturnModifier = 0x140, @@ -148,6 +149,7 @@ enum DataObjectType : uint { kImageAsset = 0xe, kMToonAsset = 0xf, kTextAsset = 0x1f, + kAVIMovieAsset = 0x24, kAssetDataChunk = 0xffff, }; @@ -752,7 +754,7 @@ protected: }; struct MovieElement : public StructuralDef { - MovieElement(); + explicit MovieElement(bool avi); // Possible flags: NotDirectToScreen, CacheBitmap, Hidden, Loop, Loop + Alternate, Paused uint32 sizeIncludingTag; @@ -776,6 +778,8 @@ struct MovieElement : public StructuralDef { Common::String name; + bool isAVI; + protected: DataReadErrorCode load(DataReader &reader) override; }; @@ -1851,6 +1855,21 @@ protected: DataReadErrorCode load(DataReader &reader) override; }; +struct AVIMovieAsset : public DataObject { + AVIMovieAsset(); + + uint8 unknown1[12]; + uint32 assetID; + uint8 unknown2[4]; + uint16 extFileNameLength; + uint8 unknown3[60]; + + Common::String extFileName; + +protected: + DataReadErrorCode load(DataReader &reader) override; +}; + struct AudioAsset : public DataObject { struct MacPart { uint8 unknown4[4]; diff --git a/engines/mtropolis/element_factory.cpp b/engines/mtropolis/element_factory.cpp index 5692ca60f0e..09341b9e593 100644 --- a/engines/mtropolis/element_factory.cpp +++ b/engines/mtropolis/element_factory.cpp @@ -64,6 +64,7 @@ SIElementFactory *getElementFactoryForDataObjectType(const Data::DataObjectTypes switch (dataObjectType) { case Data::DataObjectTypes::kGraphicElement: return ElementFactory::getInstance(); + case Data::DataObjectTypes::kAVIMovieElement: case Data::DataObjectTypes::kMovieElement: return ElementFactory::getInstance(); case Data::DataObjectTypes::kImageElement: diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp index 89b9f947444..5006b4f6cfe 100644 --- a/engines/mtropolis/elements.cpp +++ b/engines/mtropolis/elements.cpp @@ -21,7 +21,9 @@ #include "video/video_decoder.h" #include "video/qt_decoder.h" +#include "video/avi_decoder.h" +#include "common/file.h" #include "common/substream.h" #include "graphics/macgui/macfontmanager.h" @@ -618,42 +620,67 @@ void MovieElement::activate() { return; } - if (asset->getAssetType() != kAssetTypeMovie) { - warning("Movie element assigned an asset that isn't a movie"); + if (asset->getAssetType() == kAssetTypeMovie) { + MovieAsset *movieAsset = static_cast(asset.get()); + size_t streamIndex = movieAsset->getStreamIndex(); + int segmentIndex = project->getSegmentForStreamIndex(streamIndex); + project->openSegmentStream(segmentIndex); + Common::SeekableReadStream *stream = project->getStreamForSegment(segmentIndex); + + if (!stream) { + warning("Movie element stream could not be opened"); + return; + } + + Video::QuickTimeDecoder *qtDecoder = new Video::QuickTimeDecoder(); + qtDecoder->setChunkBeginOffset(movieAsset->getMovieDataPos()); + qtDecoder->setVolume(_volume * 255 / 100); + + _videoDecoder.reset(qtDecoder); + _damagedFrames = movieAsset->getDamagedFrames(); + + Common::SafeSeekableSubReadStream *movieDataStream = new Common::SafeSeekableSubReadStream(stream, movieAsset->getMovieDataPos(), movieAsset->getMovieDataPos() + movieAsset->getMovieDataSize(), DisposeAfterUse::NO); + + if (!_videoDecoder->loadStream(movieDataStream)) + _videoDecoder.reset(); + else { + if (getRuntime()->getHacks().removeQuickTimeEdits) + qtDecoder->flattenEditLists(); + + _timeScale = qtDecoder->getTimeScale(); + + _maxTimestamp = qtDecoder->getDuration().convertToFramerate(qtDecoder->getTimeScale()).totalNumberOfFrames(); + } + + _unloadSignaller = project->notifyOnSegmentUnload(segmentIndex, this); + } else if (asset->getAssetType() == kAssetTypeAVIMovie) { + AVIMovieAsset *aviAsset = static_cast(asset.get()); + + Common::File *f = new Common::File(); + if (!f->open(Common::Path(Common::String("VIDEO/") + aviAsset->getExtFileName()))) { + warning("Movie asset could not be opened"); + delete f; + return; + } + + Video::AVIDecoder *aviDec = new Video::AVIDecoder(); + aviDec->setVolume(_volume * 255 / 100); + + _videoDecoder.reset(aviDec); + + if (!_videoDecoder->loadStream(f)) + _videoDecoder.reset(); + else { + _timeScale = 1000; + _maxTimestamp = aviDec->getDuration().convertToFramerate(1000).totalNumberOfFrames(); + } + } else { + warning("Movie element referenced a non-movie asset"); return; } - MovieAsset *movieAsset = static_cast(asset.get()); - size_t streamIndex = movieAsset->getStreamIndex(); - int segmentIndex = project->getSegmentForStreamIndex(streamIndex); - project->openSegmentStream(segmentIndex); - Common::SeekableReadStream *stream = project->getStreamForSegment(segmentIndex); - - if (!stream) { - warning("Movie element stream could not be opened"); - return; - } - - Video::QuickTimeDecoder *qtDecoder = new Video::QuickTimeDecoder(); - qtDecoder->setChunkBeginOffset(movieAsset->getMovieDataPos()); - qtDecoder->setVolume(_volume * 255 / 100); - - _videoDecoder.reset(qtDecoder); - _damagedFrames = movieAsset->getDamagedFrames(); - - Common::SafeSeekableSubReadStream *movieDataStream = new Common::SafeSeekableSubReadStream(stream, movieAsset->getMovieDataPos(), movieAsset->getMovieDataPos() + movieAsset->getMovieDataSize(), DisposeAfterUse::NO); - - if (!_videoDecoder->loadStream(movieDataStream)) - _videoDecoder.reset(); - - if (getRuntime()->getHacks().removeQuickTimeEdits) - qtDecoder->flattenEditLists(); - _timeScale = qtDecoder->getTimeScale(); - - _unloadSignaller = project->notifyOnSegmentUnload(segmentIndex, this); _playMediaSignaller = project->notifyOnPlayMedia(this); - _maxTimestamp = qtDecoder->getDuration().convertToFramerate(qtDecoder->getTimeScale()).totalNumberOfFrames(); _playRange = IntRange(0, 0); _currentTimestamp = 0; @@ -704,6 +731,9 @@ void MovieElement::queueAutoPlayEvents(Runtime *runtime, bool isAutoPlaying) { void MovieElement::render(Window *window) { const IntRange realRange = computeRealRange(); + if (!_videoDecoder) + return; + if (_needsReset) { _videoDecoder->setReverse(_reversed); _videoDecoder->seek(Audio::Timestamp(0, _timeScale).addFrames(_currentTimestamp)); diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h index f391877b957..f8fcd07d5e4 100644 --- a/engines/mtropolis/runtime.h +++ b/engines/mtropolis/runtime.h @@ -3040,6 +3040,7 @@ enum AssetType { kAssetTypeImage, kAssetTypeText, kAssetTypeMToon, + kAssetTypeAVIMovie, }; class AssetHooks {