/**************************************************************************** ** libebml : parse EBML files, see http://embl.sourceforge.net/ ** ** ** ** Copyright (C) 2002-2005 Steve Lhomme. All rights reserved. ** ** This file is part of libebml. ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation; either ** version 2.1 of the License, or (at your option) any later version. ** ** This library 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 ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with this library; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** ** See http://www.matroska.org/license/lgpl/ for LGPL licensing information. ** ** Contact license@matroska.org if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ /*! \file \version \$Id: EbmlMaster.cpp 1178 2005-05-19 15:47:11Z robux4 $ \author Steve Lhomme */ #include #include #include "ebml/EbmlMaster.h" #include "ebml/EbmlStream.h" #include "ebml/EbmlContexts.h" #include "ebml/MemIOCallback.h" START_LIBEBML_NAMESPACE EbmlMaster::EbmlMaster(const EbmlSemanticContext & aContext, bool bSizeIsknown) :EbmlElement(0), Context(aContext), bChecksumUsed(bChecksumUsedByDefault) { SetSizeIsFinite(bSizeIsknown); SetValueIsSet(); ProcessMandatory(); } EbmlMaster::EbmlMaster(const EbmlMaster & ElementToClone) :EbmlElement(ElementToClone) ,ElementList(ElementToClone.ListSize()) ,Context(ElementToClone.Context) ,bChecksumUsed(ElementToClone.bChecksumUsed) ,Checksum(ElementToClone.Checksum) { // add a clone of the list std::vector::const_iterator Itr = ElementToClone.ElementList.begin(); std::vector::iterator myItr = ElementList.begin(); while (Itr != ElementToClone.ElementList.end()) { *myItr = (*Itr)->Clone(); Itr++; myItr++; } } EbmlMaster::~EbmlMaster() { assert(!IsLocked()); // you're trying to delete a locked element !!! size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if (!(*ElementList[Index]).IsLocked()) { delete ElementList[Index]; } } } /*! \todo handle exception on errors \todo write all the Mandatory elements in the Context, otherwise assert */ uint32 EbmlMaster::RenderData(IOCallback & output, bool bForceRender, bool bKeepIntact) { uint32 Result = 0; size_t Index; if (!bForceRender) { assert(CheckMandatory()); } if (!bChecksumUsed) { // old school for (Index = 0; Index < ElementList.size(); Index++) { if (!bKeepIntact && (ElementList[Index])->IsDefaultValue()) continue; Result += (ElementList[Index])->Render(output, bKeepIntact, false ,bForceRender); } } else { // new school MemIOCallback TmpBuf(GetSize() - 6); for (Index = 0; Index < ElementList.size(); Index++) { if (!bKeepIntact && (ElementList[Index])->IsDefaultValue()) continue; (ElementList[Index])->Render(TmpBuf, bKeepIntact, false ,bForceRender); } Checksum.FillCRC32(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize()); Result += Checksum.Render(output, true, false ,bForceRender); output.writeFully(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize()); Result += TmpBuf.GetDataBufferSize(); } return Result; } /*! \todo We might be able to forbid elements that don't exist in the context */ bool EbmlMaster::PushElement(EbmlElement & element) { ElementList.push_back(&element); return true; } uint64 EbmlMaster::UpdateSize(bool bKeepIntact, bool bForceRender) { SetSize_(0); if (!IsFiniteSize()) return (0-1); if (!bForceRender) { assert(CheckMandatory()); } size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if (!bKeepIntact && (ElementList[Index])->IsDefaultValue()) continue; (ElementList[Index])->UpdateSize(bKeepIntact, bForceRender); uint64 SizeToAdd = (ElementList[Index])->ElementSize(bKeepIntact); #if defined(_DEBUG) || defined(DEBUG) if (SizeToAdd == (0-1)) return (0-1); #endif // DEBUG SetSize_(GetSize() + SizeToAdd); } if (bChecksumUsed) { SetSize_(GetSize() + Checksum.ElementSize()); } return GetSize(); } uint32 EbmlMaster::WriteHead(IOCallback & output, int nSizeLength, bool bKeepIntact) { SetSizeLength(nSizeLength); return RenderHead(output, false, bKeepIntact); } /*! \todo this code is very suspicious ! */ uint64 EbmlMaster::ReadData(IOCallback & input, ScopeMode ReadFully) { input.setFilePointer(GetSize(), seek_current); return GetSize(); } /*! \note Hopefully no global element is mandatory \todo should be called for ALL EbmlMaster element on construction */ bool EbmlMaster::ProcessMandatory() { if (Context.Size == 0) { return true; } assert(Context.MyTable != NULL); unsigned int EltIdx; for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) { if (Context.MyTable[EltIdx].Mandatory && Context.MyTable[EltIdx].Unique) { assert(Context.MyTable[EltIdx].GetCallbacks.Create != NULL); PushElement(Context.MyTable[EltIdx].GetCallbacks.Create()); } } return true; } bool EbmlMaster::CheckMandatory() const { assert(Context.MyTable != NULL); unsigned int EltIdx; for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) { if (Context.MyTable[EltIdx].Mandatory) { if (FindElt(Context.MyTable[EltIdx].GetCallbacks) == NULL) { #if defined(_DEBUG) || defined(DEBUG) // you are missing this Mandatory element // const char * MissingName = Context.MyTable[EltIdx].GetCallbacks.DebugName; #endif // DEBUG return false; } } } return true; } std::vector EbmlMaster::FindAllMissingElements() { assert(Context.MyTable != NULL); std::vector missingElements; for (size_t ChildElementNo = 0; ChildElementNo < ElementList.size(); ChildElementNo++) { EbmlElement *childElement = ElementList[ChildElementNo]; if (!childElement->ValueIsSet()) { std::string missingValue; missingValue = "The Child Element \""; missingValue.append(EBML_NAME(childElement)); missingValue.append("\" of EbmlMaster \""); missingValue.append(EBML_NAME(this)); missingValue.append("\", does not have a value set."); missingElements.push_back(missingValue); } if (childElement->IsMaster()) { EbmlMaster *childMaster = (EbmlMaster *)childElement; std::vector childMissingElements = childMaster->FindAllMissingElements(); for (size_t s = 0; s < childMissingElements.size(); s++) missingElements.push_back(childMissingElements[s]); } } unsigned int EltIdx; for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) { if (Context.MyTable[EltIdx].Mandatory) { if (FindElt(Context.MyTable[EltIdx].GetCallbacks) == NULL) { std::string missingElement; missingElement = "Missing element \""; missingElement.append(Context.MyTable[EltIdx].GetCallbacks.DebugName); missingElement.append("\" in EbmlMaster \""); missingElement.append(Context.MasterElt->DebugName); missingElement.append("\""); missingElements.push_back(missingElement); } } } return missingElements; } EbmlElement *EbmlMaster::FindElt(const EbmlCallbacks & Callbacks) const { size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { EbmlElement * tmp = ElementList[Index]; if (EbmlId(*tmp) == Callbacks.GlobalId) return tmp; } return NULL; } EbmlElement *EbmlMaster::FindFirstElt(const EbmlCallbacks & Callbacks, bool bCreateIfNull) { size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if (EbmlId(*(ElementList[Index])) == Callbacks.GlobalId) return ElementList[Index]; } if (bCreateIfNull && Callbacks.Create != NULL) { // add the element EbmlElement *NewElt = &(Callbacks.Create()); if (NewElt == NULL) return NULL; if (!PushElement(*NewElt)) { delete NewElt; NewElt = NULL; } return NewElt; } return NULL; } EbmlElement *EbmlMaster::FindFirstElt(const EbmlCallbacks & Callbacks) const { size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if (EbmlId(*(ElementList[Index])) == Callbacks.GlobalId) return ElementList[Index]; } return NULL; } /*! \todo only return elements that are from the same type ! \todo the element might be the unique in the context ! */ EbmlElement *EbmlMaster::FindNextElt(const EbmlElement & PastElt, bool bCreateIfNull) { size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if ((ElementList[Index]) == &PastElt) { // found past element, new one is : Index++; break; } } while (Index < ElementList.size()) { if ((EbmlId)PastElt == (EbmlId)(*ElementList[Index])) break; Index++; } if (Index != ElementList.size()) return ElementList[Index]; if (bCreateIfNull && PastElt.Generic().Create != NULL) { // add the element EbmlElement *NewElt = &(PastElt.Generic().Create()); if (NewElt == NULL) return NULL; if (!PushElement(*NewElt)) { delete NewElt; NewElt = NULL; } return NewElt; } return NULL; } EbmlElement *EbmlMaster::FindNextElt(const EbmlElement & PastElt) const { size_t Index; for (Index = 0; Index < ElementList.size(); Index++) { if ((ElementList[Index]) == &PastElt) { // found past element, new one is : Index++; break; } } while (Index < ElementList.size()) { if ((EbmlId)PastElt == (EbmlId)(*ElementList[Index])) return ElementList[Index]; Index++; } return NULL; } EbmlElement *EbmlMaster::AddNewElt(const EbmlCallbacks & Callbacks) { // add the element EbmlElement *NewElt = &(Callbacks.Create()); if (NewElt == NULL) return NULL; if (!PushElement(*NewElt)) { delete NewElt; NewElt = NULL; } return NewElt; } void EbmlMaster::Sort() { std::sort(ElementList.begin(), ElementList.end(), EbmlElement::CompareElements); } /*! \brief Method to help reading a Master element and all subsequent children quickly \todo add an option to discard even unknown elements \todo handle when a mandatory element is not found */ void EbmlMaster::Read(EbmlStream & inDataStream, const EbmlSemanticContext & sContext, int & UpperEltFound, EbmlElement * & FoundElt, bool AllowDummyElt, ScopeMode ReadFully) { if (ReadFully != SCOPE_NO_DATA) { EbmlElement * ElementLevelA; // remove all existing elements, including the mandatory ones... size_t Index; for (Index=0; Index 0) { inDataStream.I_O().setFilePointer(GetSizePosition() + GetSizeLength(), seek_beginning); ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt); while (ElementLevelA != NULL && MaxSizeToRead > 0 && UpperEltFound <= 0) { MaxSizeToRead = GetEndPosition() - ElementLevelA->GetEndPosition(); // even if it's the default value if (!AllowDummyElt && ElementLevelA->IsDummy()) { ElementLevelA->SkipData(inDataStream, sContext); delete ElementLevelA; // forget this unknown element } else { // more logical to do it afterward ElementList.push_back(ElementLevelA); ElementLevelA->Read(inDataStream, EBML_CONTEXT(ElementLevelA), UpperEltFound, FoundElt, AllowDummyElt, ReadFully); // just in case ElementLevelA->SkipData(inDataStream, EBML_CONTEXT(ElementLevelA)); } if (UpperEltFound > 0) { UpperEltFound--; if (UpperEltFound > 0 || MaxSizeToRead <= 0) goto processCrc; ElementLevelA = FoundElt; continue; } if (UpperEltFound < 0) { UpperEltFound++; if (UpperEltFound < 0) goto processCrc; } if (MaxSizeToRead <= 0) { goto processCrc;// this level is finished } ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt); } if (UpperEltFound > 0) { FoundElt = ElementLevelA; } } processCrc: for (Index=0; Index(ElementList[Index])); delete ElementList[Index]; Remove(Index--); } } SetValueIsSet(); } } void EbmlMaster::Remove(size_t Index) { if (Index < ElementList.size()) { std::vector::iterator Itr = ElementList.begin(); while (Index-- > 0) { Itr++; } ElementList.erase(Itr); } } bool EbmlMaster::VerifyChecksum() const { if (!bChecksumUsed) return true; EbmlCrc32 aChecksum; /// \todo remove the Checksum if it's in the list /// \todo find another way when not all default values are saved or (unknown from the reader !!!) MemIOCallback TmpBuf(GetSize() - 6); for (size_t Index = 0; Index < ElementList.size(); Index++) { (ElementList[Index])->Render(TmpBuf, true, false, true); } aChecksum.FillCRC32(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize()); return (aChecksum.GetCrc32() == Checksum.GetCrc32()); } bool EbmlMaster::InsertElement(EbmlElement & element, size_t position) { std::vector::iterator Itr = ElementList.begin(); while (Itr != ElementList.end() && position--) { Itr++; } if ((Itr == ElementList.end()) && position) return false; ElementList.insert(Itr, &element); return true; } bool EbmlMaster::InsertElement(EbmlElement & element, const EbmlElement & before) { std::vector::iterator Itr = ElementList.begin(); while (Itr != ElementList.end() && *Itr != &before) { Itr++; } if (Itr == ElementList.end()) return false; ElementList.insert(Itr, &element); return true; } END_LIBEBML_NAMESPACE