diff --git a/matroska/ebmlelement.cpp b/matroska/ebmlelement.cpp index 1617c4f..e307bd2 100644 --- a/matroska/ebmlelement.cpp +++ b/matroska/ebmlelement.cpp @@ -77,6 +77,7 @@ void EbmlElement::internalParse() throw TruncatedDataException(); } stream().seekg(startOffset()); + // read ID char buf[maximumIdLengthSupported() > maximumSizeLengthSupported() ? maximumIdLengthSupported() : maximumSizeLengthSupported()] = {0}; byte beg = static_cast(stream().peek()), mask = 0x80; @@ -100,10 +101,50 @@ void EbmlElement::internalParse() reader().read(buf + (maximumIdLengthSupported() - m_idLength), m_idLength); m_id = BE::toUInt32(buf); + // check whether this element is actually a sibling of one of its parents rather then a child + // (might be the case if the parent's size is unknown and hence assumed to be the max file size) + if(m_parent && m_parent->m_sizeUnknown) { + // check at which level in the hierarchy the element is supposed to occour using its ID + // (the only chance to find out whether the element belongs higher up in the hierarchy) + const MatroskaElementLevel supposedLevel = matroskaIdLevel(m_id); + const byte actualLevel = level(); + if(actualLevel > supposedLevel) { + // the file belongs higher up in the hierarchy so find a better parent + if(EbmlElement *betterParent = m_parent->parent(actualLevel - static_cast(supposedLevel))) { + // recompute the parent size (assumption - which was rest of the available space - was wrong) + m_parent->m_dataSize = m_startOffset - m_parent->m_startOffset - m_parent->headerSize(); + m_parent->m_sizeUnknown = false; + // detatch from ... + if(m_parent->firstChild() == this) { + // ... parent + m_parent->m_firstChild.release(); + m_parent->m_firstChild = move(m_nextSibling); + } else { + // ... previous sibling + for(EbmlElement *sibling = m_parent->firstChild(); sibling; sibling = sibling->nextSibling()) { + if(sibling->nextSibling() == this) { + sibling->m_nextSibling.release(); + sibling->m_nextSibling = move(m_nextSibling); + break; + } + } + } + // insert as child of better parent + if(EbmlElement *previousSibling = betterParent->lastChild()) { + previousSibling->m_nextSibling.reset(this); + } else { + betterParent->m_firstChild.reset(this); + } + // update own reference to parent + m_parent = betterParent; + } + } + } + // read size beg = static_cast(stream().peek()), mask = 0x80; m_sizeLength = 1; - if(beg == 0xFF) { + if((m_sizeUnknown = (beg == 0xFF))) { // this indicates that the element size is unknown // -> just assume the element takes the maximum available size m_dataSize = maxTotalSize() - headerSize(); diff --git a/tests/overallmkv.cpp b/tests/overallmkv.cpp index ec1c294..03a8038 100644 --- a/tests/overallmkv.cpp +++ b/tests/overallmkv.cpp @@ -152,8 +152,7 @@ void OverallTests::checkMkvTestfile3() /*! * \brief Checks "matroska_wave1/test4.mkv". - * \remarks This file is using the EBML feature that allows Master elements to have no known size. Handling - * such files is not supported yet. + * \remarks This file is using the EBML feature that allows Master elements to have no known size. */ void OverallTests::checkMkvTestfile4() {