diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 74f0d179..5cb7fad0 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -116,9 +116,9 @@ FrameFactory *FrameFactory::instance() return &factory; } -Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const +std::pair FrameFactory::prepareFrameHeader( + ByteVector &data, const Header *tagHeader) const { - ByteVector data = origData; unsigned int version = tagHeader->majorVersion(); auto header = new Frame::Header(data, version); ByteVector frameID = header->frameID(); @@ -131,7 +131,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe header->frameSize() > data.size()) { delete header; - return nullptr; + return {nullptr, false}; } #ifndef NO_ITUNES_HACKS @@ -148,7 +148,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe for(auto it = frameID.cbegin(); it != frameID.cend(); it++) { if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { delete header; - return nullptr; + return {nullptr, false}; } } @@ -165,24 +165,39 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe if(!zlib::isAvailable() && header->compression()) { debug("Compressed frames are currently not supported."); - return new UnknownFrame(data, header); + return {header, false}; } if(header->encryption()) { debug("Encrypted frames are currently not supported."); - return new UnknownFrame(data, header); + return {header, false}; } if(!updateFrame(header)) { header->setTagAlterPreservation(true); - return new UnknownFrame(data, header); + return {header, false}; } - // updateFrame() might have updated the frame ID. + return {header, true}; +} - frameID = header->frameID(); +Frame *FrameFactory::createFrame(const ByteVector &origData, + const Header *tagHeader) const +{ + ByteVector data = origData; + auto [header, ok] = prepareFrameHeader(data, tagHeader); + if(!ok) { + // check if frame is valid and return as UnknownFrame + return header ? new UnknownFrame(data, header) : nullptr; + } + return createFrame(data, header, tagHeader); +} - // This is where things get necissarily nasty. Here we determine which +Frame *FrameFactory::createFrame(const ByteVector &data, Frame::Header *header, + const Header *tagHeader) const { + ByteVector frameID = header->frameID(); + + // This is where things get necessarily nasty. Here we determine which // Frame subclass (or if none is found simply an Frame) based // on the frame ID. Since there are a lot of possibilities, that means // a lot of if blocks. diff --git a/taglib/mpeg/id3v2/id3v2framefactory.h b/taglib/mpeg/id3v2/id3v2framefactory.h index 049daa84..7a2ed610 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/taglib/mpeg/id3v2/id3v2framefactory.h @@ -127,6 +127,31 @@ namespace TagLib { */ virtual bool updateFrame(Frame::Header *header) const; + /*! + * Creates and prepares the frame header for createFrame(). + * + * \param data data of the frame (might be modified) + * \param tagHeader the tag header + * \param header reference to frame header pointer, will be set to + * nullptr if the frame is invalid + * \return {header, ok}: header is a created frame header or nullptr + * if the frame is invalid; ok is true if the frame is supported. + */ + std::pair prepareFrameHeader( + ByteVector &data, const Header *tagHeader) const; + + /*! + * Create a frame based on \a data. \a header should be a valid frame + * header and \a tagHeader a valid ID3v2::Header instance. + * + * This method is called by the public overloaded method + * createFrame(const ByteVector &, const Header *) after creating + * \a header from verified \a data using prepareFrameHeader(), so + * this method is provided to be reimplemented in derived classes. + */ + virtual Frame *createFrame(const ByteVector &data, Frame::Header *header, + const Header *tagHeader) const; + private: FrameFactory(const FrameFactory &) = delete; FrameFactory &operator=(const FrameFactory &) = delete;