3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / plugins / matroska / libebml / EbmlMaster.cpp
blob3efe21aa5b1d045d787ddc21af0ac239c18fe8a5
1 /****************************************************************************
2 ** libebml : parse EBML files, see http://embl.sourceforge.net/
3 **
4 ** <file/class description>
5 **
6 ** Copyright (C) 2002-2005 Steve Lhomme. All rights reserved.
7 **
8 ** This file is part of libebml.
9 **
10 ** This library is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU Lesser General Public
12 ** License as published by the Free Software Foundation; either
13 ** version 2.1 of the License, or (at your option) any later version.
14 **
15 ** This library is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** Lesser General Public License for more details.
19 **
20 ** You should have received a copy of the GNU Lesser General Public
21 ** License along with this library; if not, write to the Free Software
22 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 ** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.
26 ** Contact license@matroska.org if any conditions of this licensing are
27 ** not clear to you.
29 **********************************************************************/
31 /*!
32 \file
33 \version \$Id: EbmlMaster.cpp 1178 2005-05-19 15:47:11Z robux4 $
34 \author Steve Lhomme <robux4 @ users.sf.net>
37 #include <cassert>
38 #include <algorithm>
40 #include "ebml/EbmlMaster.h"
41 #include "ebml/EbmlStream.h"
42 #include "ebml/EbmlContexts.h"
43 #include "ebml/MemIOCallback.h"
45 START_LIBEBML_NAMESPACE
47 EbmlMaster::EbmlMaster(const EbmlSemanticContext & aContext, const bool bSizeIsknown)
48 :EbmlElement(0), Context(aContext), bChecksumUsed(bChecksumUsedByDefault)
50 bSizeIsFinite = bSizeIsknown;
51 bValueIsSet = true;
52 ProcessMandatory();
55 EbmlMaster::EbmlMaster(const EbmlMaster & ElementToClone)
56 :EbmlElement(ElementToClone)
57 ,ElementList(ElementToClone.ListSize())
58 ,Context(ElementToClone.Context)
59 ,bChecksumUsed(ElementToClone.bChecksumUsed)
60 ,Checksum(ElementToClone.Checksum)
62 // add a clone of the list
63 std::vector<EbmlElement *>::const_iterator Itr = ElementToClone.ElementList.begin();
64 std::vector<EbmlElement *>::iterator myItr = ElementList.begin();
65 while (Itr != ElementToClone.ElementList.end())
67 *myItr = (*Itr)->Clone();
68 Itr++; myItr++;
73 EbmlMaster::~EbmlMaster()
75 assert(!bLocked); // you're trying to delete a locked element !!!
77 size_t Index;
79 for (Index = 0; Index < ElementList.size(); Index++) {
80 if (!(*ElementList[Index]).IsLocked()) {
81 delete ElementList[Index];
86 /*!
87 \todo handle exception on errors
88 \todo write all the Mandatory elements in the Context, otherwise assert
90 uint32 EbmlMaster::RenderData(IOCallback & output, bool bForceRender, bool bKeepIntact)
92 uint32 Result = 0;
93 size_t Index;
95 if (!bForceRender) {
96 assert(CheckMandatory());
99 if (!bChecksumUsed) { // old school
100 for (Index = 0; Index < ElementList.size(); Index++) {
101 if (!bKeepIntact && (ElementList[Index])->IsDefaultValue())
102 continue;
103 Result += (ElementList[Index])->Render(output, bKeepIntact, false ,bForceRender);
105 } else { // new school
106 MemIOCallback TmpBuf(Size - 6);
107 for (Index = 0; Index < ElementList.size(); Index++) {
108 if (!bKeepIntact && (ElementList[Index])->IsDefaultValue())
109 continue;
110 (ElementList[Index])->Render(TmpBuf, bKeepIntact, false ,bForceRender);
112 Checksum.FillCRC32(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize());
113 Result += Checksum.Render(output, true, false ,bForceRender);
114 output.writeFully(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize());
115 Result += TmpBuf.GetDataBufferSize();
118 return Result;
122 \todo We might be able to forbid elements that don't exist in the context
124 bool EbmlMaster::PushElement(EbmlElement & element)
126 ElementList.push_back(&element);
127 return true;
130 uint64 EbmlMaster::UpdateSize(bool bKeepIntact, bool bForceRender)
132 Size = 0;
134 if (!bSizeIsFinite)
135 return (0-1);
137 if (!bForceRender) {
138 assert(CheckMandatory());
141 size_t Index;
143 for (Index = 0; Index < ElementList.size(); Index++) {
144 if (!bKeepIntact && (ElementList[Index])->IsDefaultValue())
145 continue;
146 (ElementList[Index])->UpdateSize(bKeepIntact, bForceRender);
147 uint64 SizeToAdd = (ElementList[Index])->ElementSize(bKeepIntact);
148 #if defined(_DEBUG) || defined(DEBUG)
149 if (SizeToAdd == (0-1))
150 return (0-1);
151 #endif // DEBUG
152 Size += SizeToAdd;
154 if (bChecksumUsed) {
155 Size += Checksum.ElementSize();
158 return Size;
161 uint32 EbmlMaster::WriteHead(IOCallback & output, int nSizeLength, bool bKeepIntact)
163 SetSizeLength(nSizeLength);
164 return RenderHead(output, false, bKeepIntact);
168 \todo this code is very suspicious !
170 uint64 EbmlMaster::ReadData(IOCallback & input, ScopeMode ReadFully)
172 input.setFilePointer(Size, seek_current);
173 return Size;
177 \note Hopefully no global element is mandatory
178 \todo should be called for ALL EbmlMaster element on construction
180 bool EbmlMaster::ProcessMandatory()
182 if (Context.Size == 0)
184 return true;
187 assert(Context.MyTable != NULL);
189 unsigned int EltIdx;
190 for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) {
191 if (Context.MyTable[EltIdx].Mandatory && Context.MyTable[EltIdx].Unique) {
192 assert(Context.MyTable[EltIdx].GetCallbacks.Create != NULL);
193 PushElement(Context.MyTable[EltIdx].GetCallbacks.Create());
196 return true;
199 bool EbmlMaster::CheckMandatory() const
201 assert(Context.MyTable != NULL);
203 unsigned int EltIdx;
204 for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) {
205 if (Context.MyTable[EltIdx].Mandatory) {
206 if (FindElt(Context.MyTable[EltIdx].GetCallbacks) == NULL) {
207 #if defined(_DEBUG) || defined(DEBUG)
208 // you are missing this Mandatory element
209 // const char * MissingName = Context.MyTable[EltIdx].GetCallbacks.DebugName;
210 #endif // DEBUG
211 return false;
216 return true;
219 std::vector<std::string> EbmlMaster::FindAllMissingElements()
221 assert(Context.MyTable != NULL);
223 std::vector<std::string> missingElements;
225 for (size_t ChildElementNo = 0; ChildElementNo < ElementList.size(); ChildElementNo++) {
226 EbmlElement *childElement = ElementList[ChildElementNo];
227 if (!childElement->ValueIsSet()) {
228 std::string missingValue;
229 missingValue = "The Child Element \"";
230 missingValue.append(childElement->Generic().DebugName);
231 missingValue.append("\" of EbmlMaster \"");
232 missingValue.append(this->Generic().DebugName);
233 missingValue.append("\", does not have a value set.");
234 missingElements.push_back(missingValue);
237 if (childElement->IsMaster()) {
238 EbmlMaster *childMaster = (EbmlMaster *)childElement;
240 std::vector<std::string> childMissingElements = childMaster->FindAllMissingElements();
241 for (size_t s = 0; s < childMissingElements.size(); s++)
242 missingElements.push_back(childMissingElements[s]);
245 unsigned int EltIdx;
246 for (EltIdx = 0; EltIdx < Context.Size; EltIdx++) {
247 if (Context.MyTable[EltIdx].Mandatory) {
248 if (FindElt(Context.MyTable[EltIdx].GetCallbacks) == NULL) {
249 std::string missingElement;
250 missingElement = "Missing element \"";
251 missingElement.append(Context.MyTable[EltIdx].GetCallbacks.DebugName);
252 missingElement.append("\" in EbmlMaster \"");
253 missingElement.append(Context.MasterElt->DebugName);
254 missingElement.append("\"");
255 missingElements.push_back(missingElement);
260 return missingElements;
263 EbmlElement *EbmlMaster::FindElt(const EbmlCallbacks & Callbacks) const
265 size_t Index;
267 for (Index = 0; Index < ElementList.size(); Index++) {
268 EbmlElement * tmp = ElementList[Index];
269 if (EbmlId(*tmp) == Callbacks.GlobalId)
270 return tmp;
273 return NULL;
276 EbmlElement *EbmlMaster::FindFirstElt(const EbmlCallbacks & Callbacks, const bool bCreateIfNull)
278 size_t Index;
280 for (Index = 0; Index < ElementList.size(); Index++) {
281 if (EbmlId(*(ElementList[Index])) == Callbacks.GlobalId)
282 return ElementList[Index];
285 if (bCreateIfNull && Callbacks.Create != NULL) {
286 // add the element
287 EbmlElement *NewElt = &(Callbacks.Create());
288 if (NewElt == NULL)
289 return NULL;
291 if (!PushElement(*NewElt)) {
292 delete NewElt;
293 NewElt = NULL;
295 return NewElt;
298 return NULL;
301 EbmlElement *EbmlMaster::FindFirstElt(const EbmlCallbacks & Callbacks) const
303 size_t Index;
305 for (Index = 0; Index < ElementList.size(); Index++) {
306 if (EbmlId(*(ElementList[Index])) == Callbacks.GlobalId)
307 return ElementList[Index];
310 return NULL;
314 \todo only return elements that are from the same type !
315 \todo the element might be the unique in the context !
317 EbmlElement *EbmlMaster::FindNextElt(const EbmlElement & PastElt, const bool bCreateIfNull)
319 size_t Index;
321 for (Index = 0; Index < ElementList.size(); Index++) {
322 if ((ElementList[Index]) == &PastElt) {
323 // found past element, new one is :
324 Index++;
325 break;
329 while (Index < ElementList.size()) {
330 if (PastElt.Generic().GlobalId == ElementList[Index]->Generic().GlobalId)
331 break;
332 Index++;
335 if (Index != ElementList.size())
336 return ElementList[Index];
338 if (bCreateIfNull && PastElt.Generic().Create != NULL) {
339 // add the element
340 EbmlElement *NewElt = &(PastElt.Generic().Create());
341 if (NewElt == NULL)
342 return NULL;
344 if (!PushElement(*NewElt)) {
345 delete NewElt;
346 NewElt = NULL;
348 return NewElt;
351 return NULL;
354 EbmlElement *EbmlMaster::FindNextElt(const EbmlElement & PastElt) const
356 size_t Index;
358 for (Index = 0; Index < ElementList.size(); Index++) {
359 if ((ElementList[Index]) == &PastElt) {
360 // found past element, new one is :
361 Index++;
362 break;
366 while (Index < ElementList.size()) {
367 if (PastElt.Generic().GlobalId == ElementList[Index]->Generic().GlobalId)
368 return ElementList[Index];
369 Index++;
372 return NULL;
375 EbmlElement *EbmlMaster::AddNewElt(const EbmlCallbacks & Callbacks)
377 // add the element
378 EbmlElement *NewElt = &(Callbacks.Create());
379 if (NewElt == NULL)
380 return NULL;
382 if (!PushElement(*NewElt)) {
383 delete NewElt;
384 NewElt = NULL;
386 return NewElt;
389 void EbmlMaster::Sort()
391 std::sort(ElementList.begin(), ElementList.end(), EbmlElement::CompareElements);
395 \brief Method to help reading a Master element and all subsequent children quickly
396 \todo add an option to discard even unknown elements
397 \todo handle when a mandatory element is not found
399 void EbmlMaster::Read(EbmlStream & inDataStream, const EbmlSemanticContext & sContext, int & UpperEltFound, EbmlElement * & FoundElt, bool AllowDummyElt, ScopeMode ReadFully)
401 if (ReadFully != SCOPE_NO_DATA)
403 EbmlElement * ElementLevelA;
404 // remove all existing elements, including the mandatory ones...
405 size_t Index;
406 for (Index=0; Index<ElementList.size(); Index++) {
407 if (!(*ElementList[Index]).IsLocked()) {
408 delete ElementList[Index];
411 ElementList.clear();
412 uint64 MaxSizeToRead = Size;
414 // read blocks and discard the ones we don't care about
415 if (MaxSizeToRead > 0) {
416 inDataStream.I_O().setFilePointer(SizePosition + SizeLength, seek_beginning);
417 ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt);
418 while (ElementLevelA != NULL && MaxSizeToRead > 0 && UpperEltFound <= 0) {
419 MaxSizeToRead = GetEndPosition() - ElementLevelA->GetEndPosition(); // even if it's the default value
420 if (!AllowDummyElt && ElementLevelA->IsDummy()) {
421 ElementLevelA->SkipData(inDataStream, sContext);
422 delete ElementLevelA; // forget this unknown element
423 } else {
424 // more logical to do it afterward
425 ElementList.push_back(ElementLevelA);
427 ElementLevelA->Read(inDataStream, ElementLevelA->Generic().Context, UpperEltFound, FoundElt, AllowDummyElt, ReadFully);
429 // just in case
430 ElementLevelA->SkipData(inDataStream, ElementLevelA->Generic().Context);
433 if (UpperEltFound > 0) {
434 UpperEltFound--;
435 if (UpperEltFound > 0 || MaxSizeToRead <= 0)
436 goto processCrc;
437 ElementLevelA = FoundElt;
438 continue;
441 if (UpperEltFound < 0) {
442 UpperEltFound++;
443 if (UpperEltFound < 0)
444 goto processCrc;
447 if (MaxSizeToRead <= 0) {
448 goto processCrc;// this level is finished
451 ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt);
453 if (UpperEltFound > 0) {
454 FoundElt = ElementLevelA;
457 processCrc:
458 for (Index=0; Index<ElementList.size(); Index++) {
459 if (ElementList[Index]->Generic().GlobalId == EbmlCrc32::ClassInfos.GlobalId) {
460 bChecksumUsed = true;
461 // remove the element
462 Checksum = *(static_cast<EbmlCrc32*>(ElementList[Index]));
463 delete ElementList[Index];
464 Remove(Index--);
467 bValueIsSet = true;
471 void EbmlMaster::Remove(size_t Index)
473 if (Index < ElementList.size()) {
474 std::vector<EbmlElement *>::iterator Itr = ElementList.begin();
475 while (Index-- > 0) {
476 Itr++;
479 ElementList.erase(Itr);
483 bool EbmlMaster::VerifyChecksum() const
485 if (!bChecksumUsed)
486 return true;
488 EbmlCrc32 aChecksum;
489 /// \todo remove the Checksum if it's in the list
490 /// \todo find another way when not all default values are saved or (unknown from the reader !!!)
491 MemIOCallback TmpBuf(Size - 6);
492 for (size_t Index = 0; Index < ElementList.size(); Index++) {
493 (ElementList[Index])->Render(TmpBuf, true, false, true);
495 aChecksum.FillCRC32(TmpBuf.GetDataBuffer(), TmpBuf.GetDataBufferSize());
496 return (aChecksum.GetCrc32() == Checksum.GetCrc32());
499 bool EbmlMaster::InsertElement(EbmlElement & element, size_t position)
501 std::vector<EbmlElement *>::iterator Itr = ElementList.begin();
502 while (Itr != ElementList.end() && position--)
504 Itr++;
506 if ((Itr == ElementList.end()) && position)
507 return false;
509 ElementList.insert(Itr, &element);
510 return true;
513 bool EbmlMaster::InsertElement(EbmlElement & element, const EbmlElement & before)
515 std::vector<EbmlElement *>::iterator Itr = ElementList.begin();
516 while (Itr != ElementList.end() && *Itr != &before)
518 Itr++;
520 if (Itr == ElementList.end())
521 return false;
523 ElementList.insert(Itr, &element);
524 return true;
528 END_LIBEBML_NAMESPACE