| 1 | /**************************************************************************** |
|---|
| 2 | ** libebml : parse EBML files, see http://embl.sourceforge.net/ |
|---|
| 3 | ** |
|---|
| 4 | ** <file/class description> |
|---|
| 5 | ** |
|---|
| 6 | ** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved. |
|---|
| 7 | ** |
|---|
| 8 | ** This library is free software; you can redistribute it and/or |
|---|
| 9 | ** modify it under the terms of the GNU Lesser General Public |
|---|
| 10 | ** License as published by the Free Software Foundation; either |
|---|
| 11 | ** version 2.1 of the License, or (at your option) any later version. |
|---|
| 12 | ** |
|---|
| 13 | ** This library is distributed in the hope that it will be useful, |
|---|
| 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 16 | ** Lesser General Public License for more details. |
|---|
| 17 | ** |
|---|
| 18 | ** You should have received a copy of the GNU Lesser General Public |
|---|
| 19 | ** License along with this library; if not, write to the Free Software |
|---|
| 20 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 | ** |
|---|
| 22 | ** See http://www.matroska.org/license/lgpl/ for LGPL licensing information. |
|---|
| 23 | ** |
|---|
| 24 | ** Contact license@matroska.org if any conditions of this licensing are |
|---|
| 25 | ** not clear to you. |
|---|
| 26 | ** |
|---|
| 27 | **********************************************************************/ |
|---|
| 28 | |
|---|
| 29 | /*! |
|---|
| 30 | \file |
|---|
| 31 | \version \$Id$ |
|---|
| 32 | \author Steve Lhomme <robux4 @ users.sf.net> |
|---|
| 33 | */ |
|---|
| 34 | |
|---|
| 35 | #include <cassert> |
|---|
| 36 | #include <cstring> |
|---|
| 37 | |
|---|
| 38 | #include "ebml/EbmlElement.h" |
|---|
| 39 | #include "ebml/EbmlMaster.h" |
|---|
| 40 | #include "ebml/EbmlStream.h" |
|---|
| 41 | #include "ebml/EbmlVoid.h" |
|---|
| 42 | #include "ebml/EbmlDummy.h" |
|---|
| 43 | #include "ebml/EbmlContexts.h" |
|---|
| 44 | |
|---|
| 45 | START_LIBEBML_NAMESPACE |
|---|
| 46 | |
|---|
| 47 | /*! |
|---|
| 48 | \todo handle more than CodedSize of 5 |
|---|
| 49 | */ |
|---|
| 50 | int CodedSizeLength(uint64 Length, unsigned int SizeLength, bool bSizeFinite) |
|---|
| 51 | { |
|---|
| 52 | unsigned int CodedSize; |
|---|
| 53 | if (bSizeFinite) { |
|---|
| 54 | // prepare the head of the size (000...01xxxxxx) |
|---|
| 55 | // optimal size |
|---|
| 56 | if (Length < 127) // 2^7 - 1 |
|---|
| 57 | CodedSize = 1; |
|---|
| 58 | else if (Length < 16383) // 2^14 - 1 |
|---|
| 59 | CodedSize = 2; |
|---|
| 60 | else if (Length < 2097151L) // 2^21 - 1 |
|---|
| 61 | CodedSize = 3; |
|---|
| 62 | else if (Length < 268435455L) // 2^28 - 1 |
|---|
| 63 | CodedSize = 4; |
|---|
| 64 | else CodedSize = 5; |
|---|
| 65 | } else { |
|---|
| 66 | if (Length <= 127) // 2^7 - 1 |
|---|
| 67 | CodedSize = 1; |
|---|
| 68 | else if (Length <= 16383) // 2^14 - 1 |
|---|
| 69 | CodedSize = 2; |
|---|
| 70 | else if (Length <= 2097151L) // 2^21 - 1 |
|---|
| 71 | CodedSize = 3; |
|---|
| 72 | else if (Length <= 268435455L) // 2^28 - 1 |
|---|
| 73 | CodedSize = 4; |
|---|
| 74 | else CodedSize = 5; |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | if (SizeLength > 0 && CodedSize < SizeLength) { |
|---|
| 78 | // defined size |
|---|
| 79 | CodedSize = SizeLength; |
|---|
| 80 | } |
|---|
| 81 | |
|---|
| 82 | return CodedSize; |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /*! |
|---|
| 86 | \todo handle more than CodedSize of 5 |
|---|
| 87 | */ |
|---|
| 88 | int CodedSizeLengthSigned(int64 Length, unsigned int SizeLength) |
|---|
| 89 | { |
|---|
| 90 | unsigned int CodedSize; |
|---|
| 91 | // prepare the head of the size (000...01xxxxxx) |
|---|
| 92 | // optimal size |
|---|
| 93 | if (Length > -64 && Length < 64) // 2^6 |
|---|
| 94 | CodedSize = 1; |
|---|
| 95 | else if (Length > -8192 && Length < 8192) // 2^13 |
|---|
| 96 | CodedSize = 2; |
|---|
| 97 | else if (Length > -1048576L && Length < 1048576L) // 2^20 |
|---|
| 98 | CodedSize = 3; |
|---|
| 99 | else if (Length > -134217728L && Length < 134217728L) // 2^27 |
|---|
| 100 | CodedSize = 4; |
|---|
| 101 | else CodedSize = 5; |
|---|
| 102 | |
|---|
| 103 | if (SizeLength > 0 && CodedSize < SizeLength) { |
|---|
| 104 | // defined size |
|---|
| 105 | CodedSize = SizeLength; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | return CodedSize; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | int CodedValueLength(uint64 Length, int CodedSize, binary * OutBuffer) |
|---|
| 112 | { |
|---|
| 113 | int _SizeMask = 0xFF; |
|---|
| 114 | OutBuffer[0] = 1 << (8 - CodedSize); |
|---|
| 115 | for (int i=1; i<CodedSize; i++) { |
|---|
| 116 | OutBuffer[CodedSize-i] = Length & 0xFF; |
|---|
| 117 | Length >>= 8; |
|---|
| 118 | _SizeMask >>= 1; |
|---|
| 119 | } |
|---|
| 120 | // first one use a OR with the "EBML size head" |
|---|
| 121 | OutBuffer[0] |= Length & 0xFF & _SizeMask; |
|---|
| 122 | return CodedSize; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | int CodedValueLengthSigned(int64 Length, int CodedSize, binary * OutBuffer) |
|---|
| 126 | { |
|---|
| 127 | if (Length > -64 && Length < 64) // 2^6 |
|---|
| 128 | Length += 63; |
|---|
| 129 | else if (Length > -8192 && Length < 8192) // 2^13 |
|---|
| 130 | Length += 8191; |
|---|
| 131 | else if (Length > -1048576L && Length < 1048576L) // 2^20 |
|---|
| 132 | Length += 1048575L; |
|---|
| 133 | else if (Length > -134217728L && Length < 134217728L) // 2^27 |
|---|
| 134 | Length += 134217727L; |
|---|
| 135 | |
|---|
| 136 | return CodedValueLength(Length, CodedSize, OutBuffer); |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | uint64 ReadCodedSizeValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown) |
|---|
| 140 | { |
|---|
| 141 | binary SizeBitMask = 1 << 7; |
|---|
| 142 | uint64 Result = 0x7F; |
|---|
| 143 | unsigned int SizeIdx, PossibleSizeLength = 0; |
|---|
| 144 | binary PossibleSize[8]; |
|---|
| 145 | |
|---|
| 146 | SizeUnknown = 0x7F; // the last bit is discarded when computing the size |
|---|
| 147 | for (SizeIdx = 0; SizeIdx < BufferSize && SizeIdx < 8; SizeIdx++) { |
|---|
| 148 | if (InBuffer[0] & (SizeBitMask >> SizeIdx)) { |
|---|
| 149 | // ID found |
|---|
| 150 | PossibleSizeLength = SizeIdx + 1; |
|---|
| 151 | SizeBitMask >>= SizeIdx; |
|---|
| 152 | for (SizeIdx = 0; SizeIdx < PossibleSizeLength; SizeIdx++) { |
|---|
| 153 | PossibleSize[SizeIdx] = InBuffer[SizeIdx]; |
|---|
| 154 | } |
|---|
| 155 | for (SizeIdx = 0; SizeIdx < PossibleSizeLength - 1; SizeIdx++) { |
|---|
| 156 | Result <<= 7; |
|---|
| 157 | Result |= 0xFF; |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | Result = 0; |
|---|
| 161 | Result |= PossibleSize[0] & ~SizeBitMask; |
|---|
| 162 | for (unsigned int i = 1; i<PossibleSizeLength; i++) { |
|---|
| 163 | Result <<= 8; |
|---|
| 164 | Result |= PossibleSize[i]; |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | BufferSize = PossibleSizeLength; |
|---|
| 168 | |
|---|
| 169 | return Result; |
|---|
| 170 | } |
|---|
| 171 | SizeUnknown <<= 7; |
|---|
| 172 | SizeUnknown |= 0xFF; |
|---|
| 173 | } |
|---|
| 174 | |
|---|
| 175 | BufferSize = 0; |
|---|
| 176 | return 0; |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | int64 ReadCodedSizeSignedValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown) |
|---|
| 180 | { |
|---|
| 181 | int64 Result = ReadCodedSizeValue(InBuffer, BufferSize, SizeUnknown); |
|---|
| 182 | |
|---|
| 183 | if (BufferSize != 0) |
|---|
| 184 | { |
|---|
| 185 | switch (BufferSize) |
|---|
| 186 | { |
|---|
| 187 | case 1: |
|---|
| 188 | Result -= 63; |
|---|
| 189 | break; |
|---|
| 190 | case 2: |
|---|
| 191 | Result -= 8191; |
|---|
| 192 | break; |
|---|
| 193 | case 3: |
|---|
| 194 | Result -= 1048575L; |
|---|
| 195 | break; |
|---|
| 196 | case 4: |
|---|
| 197 | Result -= 134217727L; |
|---|
| 198 | break; |
|---|
| 199 | } |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | return Result; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | |
|---|
| 206 | EbmlCallbacks::EbmlCallbacks(EbmlElement & (*Creator)(), const EbmlId & aGlobalId, const char * aDebugName, const EbmlSemanticContext & aContext) |
|---|
| 207 | :Create(Creator) |
|---|
| 208 | ,GlobalId(aGlobalId) |
|---|
| 209 | ,DebugName(aDebugName) |
|---|
| 210 | ,Context(aContext) |
|---|
| 211 | { |
|---|
| 212 | assert((Create!=NULL) || !strcmp(aDebugName, "DummyElement")); |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | const EbmlSemantic & EbmlSemanticContext::GetSemantic(size_t i) const |
|---|
| 216 | { |
|---|
| 217 | assert(i<Size); |
|---|
| 218 | if (i<Size) |
|---|
| 219 | return MyTable[i]; |
|---|
| 220 | else |
|---|
| 221 | return *(EbmlSemantic*)NULL; |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | |
|---|
| 225 | EbmlElement::EbmlElement(uint64 aDefaultSize, bool bValueSet) |
|---|
| 226 | :DefaultSize(aDefaultSize) |
|---|
| 227 | ,SizeLength(0) ///< write optimal size by default |
|---|
| 228 | ,bSizeIsFinite(true) |
|---|
| 229 | ,ElementPosition(0) |
|---|
| 230 | ,SizePosition(0) |
|---|
| 231 | ,bValueIsSet(bValueSet) |
|---|
| 232 | ,DefaultIsSet(false) |
|---|
| 233 | ,bLocked(false) |
|---|
| 234 | { |
|---|
| 235 | Size = DefaultSize; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | EbmlElement::EbmlElement(const EbmlElement & ElementToClone) |
|---|
| 239 | :Size(ElementToClone.Size) |
|---|
| 240 | ,DefaultSize(ElementToClone.DefaultSize) |
|---|
| 241 | ,SizeLength(ElementToClone.SizeLength) |
|---|
| 242 | ,bSizeIsFinite(ElementToClone.bSizeIsFinite) |
|---|
| 243 | ,ElementPosition(ElementToClone.ElementPosition) |
|---|
| 244 | ,SizePosition(ElementToClone.SizePosition) |
|---|
| 245 | ,bValueIsSet(ElementToClone.bValueIsSet) |
|---|
| 246 | ,DefaultIsSet(ElementToClone.DefaultIsSet) |
|---|
| 247 | ,bLocked(ElementToClone.bLocked) |
|---|
| 248 | { |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | EbmlElement::~EbmlElement() |
|---|
| 252 | { |
|---|
| 253 | assert(!bLocked); |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | /*! |
|---|
| 257 | \todo this method is deprecated and should be called FindThisID |
|---|
| 258 | \todo replace the new RawElement with the appropriate class (when known) |
|---|
| 259 | */ |
|---|
| 260 | EbmlElement * EbmlElement::FindNextID(IOCallback & DataStream, const EbmlCallbacks & ClassInfos, uint64 MaxDataSize) |
|---|
| 261 | { |
|---|
| 262 | binary PossibleId[4]; |
|---|
| 263 | int PossibleID_Length = 0; |
|---|
| 264 | binary PossibleSize[8]; // we don't support size stored in more than 64 bits |
|---|
| 265 | uint32 PossibleSizeLength = 0; |
|---|
| 266 | uint64 SizeUnknown; |
|---|
| 267 | uint64 SizeFound; |
|---|
| 268 | bool bElementFound = false; |
|---|
| 269 | |
|---|
| 270 | binary BitMask; |
|---|
| 271 | uint64 aElementPosition, aSizePosition; |
|---|
| 272 | while (!bElementFound) { |
|---|
| 273 | // read ID |
|---|
| 274 | aElementPosition = DataStream.getFilePointer(); |
|---|
| 275 | uint32 ReadSize = 0; |
|---|
| 276 | BitMask = 1 << 7; |
|---|
| 277 | while (1) { |
|---|
| 278 | ReadSize += DataStream.read(&PossibleId[PossibleID_Length], 1); |
|---|
| 279 | if (ReadSize == uint32(PossibleID_Length)) { |
|---|
| 280 | return NULL; // no more data ? |
|---|
| 281 | } |
|---|
| 282 | if (++PossibleID_Length > 4) { |
|---|
| 283 | return NULL; // we don't support element IDs over class D |
|---|
| 284 | } |
|---|
| 285 | if (PossibleId[0] & BitMask) { |
|---|
| 286 | // this is the last octet of the ID |
|---|
| 287 | // check wether that's the one we're looking for |
|---|
| 288 | /* if (PossibleID == EBML_INFO_ID(ClassInfos)) { |
|---|
| 289 | break; |
|---|
| 290 | } else { |
|---|
| 291 | /// \todo This element should be skipped (use a context ?) |
|---|
| 292 | }*/ |
|---|
| 293 | bElementFound = true; /// \todo not exactly the one we're looking for |
|---|
| 294 | break; |
|---|
| 295 | } |
|---|
| 296 | BitMask >>= 1; |
|---|
| 297 | } |
|---|
| 298 | |
|---|
| 299 | // read the data size |
|---|
| 300 | aSizePosition = DataStream.getFilePointer(); |
|---|
| 301 | uint32 _SizeLength; |
|---|
| 302 | do { |
|---|
| 303 | if (PossibleSizeLength >= 8) |
|---|
| 304 | // Size is larger than 8 bytes |
|---|
| 305 | return NULL; |
|---|
| 306 | |
|---|
| 307 | ReadSize += DataStream.read(&PossibleSize[PossibleSizeLength++], 1); |
|---|
| 308 | _SizeLength = PossibleSizeLength; |
|---|
| 309 | SizeFound = ReadCodedSizeValue(&PossibleSize[0], _SizeLength, SizeUnknown); |
|---|
| 310 | } while (_SizeLength == 0); |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | EbmlElement *Result = NULL; |
|---|
| 314 | EbmlId PossibleID(PossibleId, PossibleID_Length); |
|---|
| 315 | if (PossibleID == EBML_INFO_ID(ClassInfos)) { |
|---|
| 316 | // the element is the one expected |
|---|
| 317 | Result = &EBML_INFO_CREATE(ClassInfos); |
|---|
| 318 | } else { |
|---|
| 319 | /// \todo find the element in the context |
|---|
| 320 | Result = new EbmlDummy(PossibleID); |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | Result->SetSizeLength(PossibleSizeLength); |
|---|
| 324 | |
|---|
| 325 | Result->Size = SizeFound; |
|---|
| 326 | |
|---|
| 327 | if (!Result->ValidateSize() || (SizeFound != SizeUnknown && MaxDataSize < Result->Size)) { |
|---|
| 328 | delete Result; |
|---|
| 329 | return NULL; |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | // check if the size is not all 1s |
|---|
| 333 | if (SizeFound == SizeUnknown) { |
|---|
| 334 | // Size of this element is unknown |
|---|
| 335 | // only possible for Master elements |
|---|
| 336 | if (!Result->SetSizeInfinite()) { |
|---|
| 337 | /// \todo the element is not allowed to be infinite |
|---|
| 338 | delete Result; |
|---|
| 339 | return NULL; |
|---|
| 340 | } |
|---|
| 341 | } else Result->SetSizeInfinite(false); |
|---|
| 342 | Result->ElementPosition = aElementPosition; |
|---|
| 343 | Result->SizePosition = aSizePosition; |
|---|
| 344 | |
|---|
| 345 | return Result; |
|---|
| 346 | } |
|---|
| 347 | |
|---|
| 348 | |
|---|
| 349 | /*! |
|---|
| 350 | \todo replace the new RawElement with the appropriate class (when known) |
|---|
| 351 | \todo skip data for Dummy elements when they are not allowed |
|---|
| 352 | \todo better check of the size checking for upper elements (using a list of size for each level) |
|---|
| 353 | \param LowLevel Will be returned with the level of the element found compared to the context given |
|---|
| 354 | */ |
|---|
| 355 | EbmlElement * EbmlElement::FindNextElement(IOCallback & DataStream, const EbmlSemanticContext & Context, int & UpperLevel, |
|---|
| 356 | uint64 MaxDataSize, bool AllowDummyElt, unsigned int MaxLowerLevel) |
|---|
| 357 | { |
|---|
| 358 | int PossibleID_Length = 0; |
|---|
| 359 | binary PossibleIdNSize[16]; |
|---|
| 360 | int PossibleSizeLength; |
|---|
| 361 | uint64 SizeUnknown; |
|---|
| 362 | int ReadIndex = 0; // trick for the algo, start index at 0 |
|---|
| 363 | uint32 ReadSize = 0; |
|---|
| 364 | uint64 SizeFound; |
|---|
| 365 | int SizeIdx; |
|---|
| 366 | bool bFound; |
|---|
| 367 | int UpperLevel_original = UpperLevel; |
|---|
| 368 | |
|---|
| 369 | do { |
|---|
| 370 | // read a potential ID |
|---|
| 371 | do { |
|---|
| 372 | assert(ReadIndex < 16); |
|---|
| 373 | // build the ID with the current Read Buffer |
|---|
| 374 | bFound = false; |
|---|
| 375 | binary IdBitMask = 1 << 7; |
|---|
| 376 | for (SizeIdx = 0; SizeIdx < ReadIndex && SizeIdx < 4; SizeIdx++) { |
|---|
| 377 | if (PossibleIdNSize[0] & (IdBitMask >> SizeIdx)) { |
|---|
| 378 | // ID found |
|---|
| 379 | PossibleID_Length = SizeIdx + 1; |
|---|
| 380 | IdBitMask >>= SizeIdx; |
|---|
| 381 | bFound = true; |
|---|
| 382 | break; |
|---|
| 383 | } |
|---|
| 384 | } |
|---|
| 385 | if (bFound) { |
|---|
| 386 | break; |
|---|
| 387 | } |
|---|
| 388 | |
|---|
| 389 | if (ReadIndex >= 4) { |
|---|
| 390 | // ID not found |
|---|
| 391 | // shift left the read octets |
|---|
| 392 | memmove(&PossibleIdNSize[0],&PossibleIdNSize[1], --ReadIndex); |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | if (DataStream.read(&PossibleIdNSize[ReadIndex++], 1) == 0) { |
|---|
| 396 | return NULL; // no more data ? |
|---|
| 397 | } |
|---|
| 398 | ReadSize++; |
|---|
| 399 | |
|---|
| 400 | } while (!bFound && MaxDataSize > ReadSize); |
|---|
| 401 | |
|---|
| 402 | SizeIdx = ReadIndex; |
|---|
| 403 | ReadIndex -= PossibleID_Length; |
|---|
| 404 | |
|---|
| 405 | // read the data size |
|---|
| 406 | uint32 _SizeLength; |
|---|
| 407 | PossibleSizeLength = ReadIndex; |
|---|
| 408 | while (1) |
|---|
| 409 | { |
|---|
| 410 | _SizeLength = PossibleSizeLength; |
|---|
| 411 | SizeFound = ReadCodedSizeValue(&PossibleIdNSize[PossibleID_Length], _SizeLength, SizeUnknown); |
|---|
| 412 | if (_SizeLength != 0) { |
|---|
| 413 | bFound = true; |
|---|
| 414 | break; |
|---|
| 415 | } |
|---|
| 416 | if (PossibleSizeLength >= 8) { |
|---|
| 417 | bFound = false; |
|---|
| 418 | break; |
|---|
| 419 | } |
|---|
| 420 | ReadSize += DataStream.read(&PossibleIdNSize[SizeIdx++], 1); |
|---|
| 421 | PossibleSizeLength++; |
|---|
| 422 | } |
|---|
| 423 | |
|---|
| 424 | if (bFound) { |
|---|
| 425 | // find the element in the context and use the correct creator |
|---|
| 426 | EbmlId PossibleID(PossibleIdNSize, PossibleID_Length); |
|---|
| 427 | EbmlElement * Result = CreateElementUsingContext(PossibleID, Context, UpperLevel, false, AllowDummyElt, MaxLowerLevel); |
|---|
| 428 | ///< \todo continue is misplaced |
|---|
| 429 | if (Result != NULL) { |
|---|
| 430 | if (AllowDummyElt || !Result->IsDummy()) { |
|---|
| 431 | Result->SetSizeLength(_SizeLength); |
|---|
| 432 | |
|---|
| 433 | Result->Size = SizeFound; |
|---|
| 434 | // UpperLevel values |
|---|
| 435 | // -1 : global element |
|---|
| 436 | // 0 : child |
|---|
| 437 | // 1 : same level |
|---|
| 438 | // + : further parent |
|---|
| 439 | if (Result->ValidateSize() && (SizeFound == SizeUnknown || UpperLevel > 0 || MaxDataSize == 0 || MaxDataSize >= (PossibleID_Length + PossibleSizeLength + SizeFound))) { |
|---|
| 440 | if (SizeFound == SizeUnknown) { |
|---|
| 441 | Result->SetSizeInfinite(); |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| 444 | Result->SizePosition = DataStream.getFilePointer() - SizeIdx + EBML_ID_LENGTH(PossibleID); |
|---|
| 445 | Result->ElementPosition = Result->SizePosition - EBML_ID_LENGTH(PossibleID); |
|---|
| 446 | // place the file at the beggining of the data |
|---|
| 447 | DataStream.setFilePointer(Result->SizePosition + _SizeLength); |
|---|
| 448 | return Result; |
|---|
| 449 | } |
|---|
| 450 | } |
|---|
| 451 | delete Result; |
|---|
| 452 | } |
|---|
| 453 | } |
|---|
| 454 | |
|---|
| 455 | // recover all the data in the buffer minus one byte |
|---|
| 456 | ReadIndex = SizeIdx - 1; |
|---|
| 457 | memmove(&PossibleIdNSize[0], &PossibleIdNSize[1], ReadIndex); |
|---|
| 458 | UpperLevel = UpperLevel_original; |
|---|
| 459 | } while ( MaxDataSize > DataStream.getFilePointer() - SizeIdx + PossibleID_Length ); |
|---|
| 460 | |
|---|
| 461 | return NULL; |
|---|
| 462 | } |
|---|
| 463 | |
|---|
| 464 | /*! |
|---|
| 465 | \todo what happens if we are in a upper element with a known size ? |
|---|
| 466 | */ |
|---|
| 467 | EbmlElement * EbmlElement::SkipData(EbmlStream & DataStream, const EbmlSemanticContext & Context, EbmlElement * TestReadElt, bool AllowDummyElt) |
|---|
| 468 | { |
|---|
| 469 | EbmlElement * Result = NULL; |
|---|
| 470 | if (bSizeIsFinite) { |
|---|
| 471 | assert(TestReadElt == NULL); |
|---|
| 472 | assert(ElementPosition < SizePosition); |
|---|
| 473 | DataStream.I_O().setFilePointer(SizePosition + CodedSizeLength(Size, SizeLength, bSizeIsFinite) + Size, seek_beginning); |
|---|
| 474 | // DataStream.I_O().setFilePointer(Size, seek_current); |
|---|
| 475 | } else { |
|---|
| 476 | ///////////////////////////////////////////////// |
|---|
| 477 | // read elements until an upper element is found |
|---|
| 478 | ///////////////////////////////////////////////// |
|---|
| 479 | bool bEndFound = false; |
|---|
| 480 | while (!bEndFound && Result == NULL) { |
|---|
| 481 | // read an element |
|---|
| 482 | /// \todo 0xFF... and true should be configurable |
|---|
| 483 | // EbmlElement * NewElt; |
|---|
| 484 | if (TestReadElt == NULL) { |
|---|
| 485 | int bUpperElement = 0; // trick to call FindNextID correctly |
|---|
| 486 | Result = DataStream.FindNextElement(Context, bUpperElement, 0xFFFFFFFFL, AllowDummyElt); |
|---|
| 487 | } else { |
|---|
| 488 | Result = TestReadElt; |
|---|
| 489 | TestReadElt = NULL; |
|---|
| 490 | } |
|---|
| 491 | |
|---|
| 492 | if (Result != NULL) { |
|---|
| 493 | unsigned int EltIndex; |
|---|
| 494 | // data known in this Master's context |
|---|
| 495 | for (EltIndex = 0; EltIndex < EBML_CTX_SIZE(Context); EltIndex++) { |
|---|
| 496 | if (EbmlId(*Result) == EBML_CTX_IDX_ID(Context,EltIndex)) { |
|---|
| 497 | // skip the data with its own context |
|---|
| 498 | Result = Result->SkipData(DataStream, EBML_SEM_CONTEXT(EBML_CTX_IDX(Context,EltIndex)), NULL); |
|---|
| 499 | break; // let's go to the next ID |
|---|
| 500 | } |
|---|
| 501 | } |
|---|
| 502 | |
|---|
| 503 | if (EltIndex >= EBML_CTX_SIZE(Context)) { |
|---|
| 504 | if (EBML_CTX_PARENT(Context) != NULL) { |
|---|
| 505 | Result = SkipData(DataStream, *EBML_CTX_PARENT(Context), Result); |
|---|
| 506 | } else { |
|---|
| 507 | assert(Context.GetGlobalContext != NULL); |
|---|
| 508 | if (Context != Context.GetGlobalContext()) { |
|---|
| 509 | Result = SkipData(DataStream, Context.GetGlobalContext(), Result); |
|---|
| 510 | } else { |
|---|
| 511 | bEndFound = true; |
|---|
| 512 | } |
|---|
| 513 | } |
|---|
| 514 | } |
|---|
| 515 | } else { |
|---|
| 516 | bEndFound = true; |
|---|
| 517 | } |
|---|
| 518 | } |
|---|
| 519 | } |
|---|
| 520 | return Result; |
|---|
| 521 | } |
|---|
| 522 | |
|---|
| 523 | EbmlElement *EbmlElement::CreateElementUsingContext(const EbmlId & aID, const EbmlSemanticContext & Context, |
|---|
| 524 | int & LowLevel, bool IsGlobalContext, bool bAllowDummy, unsigned int MaxLowerLevel) |
|---|
| 525 | { |
|---|
| 526 | unsigned int ContextIndex; |
|---|
| 527 | EbmlElement *Result = NULL; |
|---|
| 528 | |
|---|
| 529 | // elements at the current level |
|---|
| 530 | for (ContextIndex = 0; ContextIndex < EBML_CTX_SIZE(Context); ContextIndex++) { |
|---|
| 531 | if (aID == EBML_CTX_IDX_ID(Context,ContextIndex)) { |
|---|
| 532 | return &EBML_SEM_CREATE(EBML_CTX_IDX(Context,ContextIndex)); |
|---|
| 533 | } |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | // global elements |
|---|
| 537 | assert(Context.GetGlobalContext != NULL); // global should always exist, at least the EBML ones |
|---|
| 538 | const EbmlSemanticContext & tstContext = Context.GetGlobalContext(); |
|---|
| 539 | if (tstContext != Context) { |
|---|
| 540 | LowLevel--; |
|---|
| 541 | MaxLowerLevel--; |
|---|
| 542 | // recursive is good, but be carefull... |
|---|
| 543 | Result = CreateElementUsingContext(aID, tstContext, LowLevel, true, bAllowDummy, MaxLowerLevel); |
|---|
| 544 | if (Result != NULL) { |
|---|
| 545 | return Result; |
|---|
| 546 | } |
|---|
| 547 | LowLevel++; |
|---|
| 548 | MaxLowerLevel++; |
|---|
| 549 | } else { |
|---|
| 550 | return NULL; |
|---|
| 551 | } |
|---|
| 552 | |
|---|
| 553 | // parent elements |
|---|
| 554 | if (EBML_CTX_MASTER(Context) != NULL && aID == EBML_INFO_ID(*EBML_CTX_MASTER(Context))) { |
|---|
| 555 | LowLevel++; // already one level up (same as context) |
|---|
| 556 | return &EBML_INFO_CREATE(*EBML_CTX_MASTER(Context)); |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | // check wether it's not part of an upper context |
|---|
| 560 | if (EBML_CTX_PARENT(Context) != NULL) { |
|---|
| 561 | LowLevel++; |
|---|
| 562 | MaxLowerLevel++; |
|---|
| 563 | return CreateElementUsingContext(aID, *EBML_CTX_PARENT(Context), LowLevel, IsGlobalContext, bAllowDummy, MaxLowerLevel); |
|---|
| 564 | } |
|---|
| 565 | |
|---|
| 566 | if (!IsGlobalContext && bAllowDummy) { |
|---|
| 567 | LowLevel = 0; |
|---|
| 568 | Result = new EbmlDummy(aID); |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | return Result; |
|---|
| 572 | } |
|---|
| 573 | |
|---|
| 574 | /*! |
|---|
| 575 | \todo verify that the size written is the same as the data written |
|---|
| 576 | */ |
|---|
| 577 | filepos_t EbmlElement::Render(IOCallback & output, bool bWithDefault, bool bKeepPosition, bool bForceRender) |
|---|
| 578 | { |
|---|
| 579 | assert(bValueIsSet || (bWithDefault && DefaultISset())); // an element is been rendered without a value set !!! |
|---|
| 580 | // it may be a mandatory element without a default value |
|---|
| 581 | try { |
|---|
| 582 | if (!bWithDefault && IsDefaultValue()) { |
|---|
| 583 | return 0; |
|---|
| 584 | } |
|---|
| 585 | #if defined(_DEBUG) || defined(DEBUG) |
|---|
| 586 | uint64 SupposedSize = UpdateSize(bWithDefault, bForceRender); |
|---|
| 587 | #endif // _DEBUG |
|---|
| 588 | filepos_t result = RenderHead(output, bForceRender, bWithDefault, bKeepPosition); |
|---|
| 589 | uint64 WrittenSize = RenderData(output, bForceRender, bWithDefault); |
|---|
| 590 | #if defined(_DEBUG) || defined(DEBUG) |
|---|
| 591 | if (static_cast<int64>(SupposedSize) != (0-1)) assert(WrittenSize == SupposedSize); |
|---|
| 592 | #endif // DEBUG |
|---|
| 593 | result += WrittenSize; |
|---|
| 594 | return result; |
|---|
| 595 | } catch (std::exception & ex) { |
|---|
| 596 | // const char * What = ex.what(); |
|---|
| 597 | assert(false); // we should never be here ! |
|---|
| 598 | return 0; |
|---|
| 599 | } |
|---|
| 600 | } |
|---|
| 601 | |
|---|
| 602 | /*! |
|---|
| 603 | \todo store the position of the Size writing for elements with unknown size |
|---|
| 604 | \todo handle exceptions on errors |
|---|
| 605 | \todo handle CodeSize bigger than 5 bytes |
|---|
| 606 | */ |
|---|
| 607 | filepos_t EbmlElement::RenderHead(IOCallback & output, bool bForceRender, bool bWithDefault, bool bKeepPosition) |
|---|
| 608 | { |
|---|
| 609 | if (EBML_ID_LENGTH((const EbmlId&)*this) <= 0 || EBML_ID_LENGTH((const EbmlId&)*this) > 4) |
|---|
| 610 | return 0; |
|---|
| 611 | |
|---|
| 612 | UpdateSize(bWithDefault, bForceRender); |
|---|
| 613 | |
|---|
| 614 | return MakeRenderHead(output, bKeepPosition); |
|---|
| 615 | } |
|---|
| 616 | |
|---|
| 617 | filepos_t EbmlElement::MakeRenderHead(IOCallback & output, bool bKeepPosition) |
|---|
| 618 | { |
|---|
| 619 | binary FinalHead[4+8]; // Class D + 64 bits coded size |
|---|
| 620 | unsigned int FinalHeadSize; |
|---|
| 621 | |
|---|
| 622 | FinalHeadSize = EBML_ID_LENGTH((const EbmlId&)*this); |
|---|
| 623 | EbmlId(*this).Fill(FinalHead); |
|---|
| 624 | |
|---|
| 625 | int CodedSize = CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
|---|
| 626 | CodedValueLength(Size, CodedSize, &FinalHead[FinalHeadSize]); |
|---|
| 627 | FinalHeadSize += CodedSize; |
|---|
| 628 | |
|---|
| 629 | output.writeFully(FinalHead, FinalHeadSize); |
|---|
| 630 | if (!bKeepPosition) { |
|---|
| 631 | ElementPosition = output.getFilePointer() - FinalHeadSize; |
|---|
| 632 | SizePosition = ElementPosition + EBML_ID_LENGTH((const EbmlId&)*this); |
|---|
| 633 | } |
|---|
| 634 | |
|---|
| 635 | return FinalHeadSize; |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | uint64 EbmlElement::ElementSize(bool bWithDefault) const |
|---|
| 639 | { |
|---|
| 640 | if (!bWithDefault && IsDefaultValue()) |
|---|
| 641 | return 0; // won't be saved |
|---|
| 642 | return Size + EBML_ID_LENGTH((const EbmlId&)*this) + CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
|---|
| 643 | } |
|---|
| 644 | |
|---|
| 645 | bool EbmlElement::IsSmallerThan(const EbmlElement *Cmp) const |
|---|
| 646 | { |
|---|
| 647 | return EbmlId(*this) == EbmlId(*Cmp); |
|---|
| 648 | } |
|---|
| 649 | |
|---|
| 650 | bool EbmlElement::CompareElements(const EbmlElement *A, const EbmlElement *B) |
|---|
| 651 | { |
|---|
| 652 | if (EbmlId(*A) == EbmlId(*B)) |
|---|
| 653 | return A->IsSmallerThan(B); |
|---|
| 654 | else |
|---|
| 655 | return false; |
|---|
| 656 | } |
|---|
| 657 | |
|---|
| 658 | void EbmlElement::Read(EbmlStream & inDataStream, const EbmlSemanticContext & Context, int & UpperEltFound, EbmlElement * & FoundElt, bool AllowDummyElt, ScopeMode ReadFully) |
|---|
| 659 | { |
|---|
| 660 | ReadData(inDataStream.I_O(), ReadFully); |
|---|
| 661 | } |
|---|
| 662 | |
|---|
| 663 | bool EbmlElement::ForceSize(uint64 NewSize) |
|---|
| 664 | { |
|---|
| 665 | if (bSizeIsFinite) { |
|---|
| 666 | return false; |
|---|
| 667 | } |
|---|
| 668 | |
|---|
| 669 | int OldSizeLen = CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
|---|
| 670 | uint64 OldSize = Size; |
|---|
| 671 | |
|---|
| 672 | Size = NewSize; |
|---|
| 673 | |
|---|
| 674 | if (CodedSizeLength(Size, SizeLength, bSizeIsFinite) == OldSizeLen) { |
|---|
| 675 | bSizeIsFinite = true; |
|---|
| 676 | return true; |
|---|
| 677 | } |
|---|
| 678 | Size = OldSize; |
|---|
| 679 | |
|---|
| 680 | return false; |
|---|
| 681 | } |
|---|
| 682 | |
|---|
| 683 | filepos_t EbmlElement::OverwriteHead(IOCallback & output, bool bKeepPosition) |
|---|
| 684 | { |
|---|
| 685 | if (ElementPosition == 0) { |
|---|
| 686 | return 0; // the element has not been written |
|---|
| 687 | } |
|---|
| 688 | |
|---|
| 689 | uint64 CurrentPosition = output.getFilePointer(); |
|---|
| 690 | output.setFilePointer(GetElementPosition()); |
|---|
| 691 | filepos_t Result = MakeRenderHead(output, bKeepPosition); |
|---|
| 692 | output.setFilePointer(CurrentPosition); |
|---|
| 693 | return Result; |
|---|
| 694 | } |
|---|
| 695 | |
|---|
| 696 | uint64 EbmlElement::VoidMe(IOCallback & output, bool bWithDefault) |
|---|
| 697 | { |
|---|
| 698 | if (ElementPosition == 0) { |
|---|
| 699 | return 0; // the element has not been written |
|---|
| 700 | } |
|---|
| 701 | |
|---|
| 702 | EbmlVoid Dummy; |
|---|
| 703 | return Dummy.Overwrite(*this, output, bWithDefault); |
|---|
| 704 | } |
|---|
| 705 | |
|---|
| 706 | END_LIBEBML_NAMESPACE |
|---|