ChunkPath::ChunkPath( const ChunkPath& path ) { for( XMP_Int32 i=0; i<path.length(); i++ ) { this->append( path.identifier(i) ); } }
void ChunkController::findChunks( const ChunkPath& path, ChunkPath& currentPath, const Chunk& chunk ) { if( path.length() > currentPath.length() ) { for( XMP_Uns32 i=0; i<chunk.numChildren(); i++ ) { Chunk* child = NULL; try { child = chunk.getChildAt(i); } catch(...) { child = NULL; } if( child != NULL ) { currentPath.append( child->getIdentifier() ); switch( path.match( currentPath ) ) { case ChunkPath::kFullMatch: { mSearchResults.push_back( child ); } break; case ChunkPath::kPartMatch: { this->findChunks( path, currentPath, *child ); } break; case ChunkPath::kNoMatch: { // Nothing to do } break; } currentPath.remove(); } } } }//findChunks
ChunkPath::MatchResult ChunkPath::match( const ChunkPath& path ) const { MatchResult ret = kNoMatch; if( path.length() > 0 ) { XMP_Int32 depth = ( this->length() > path.length() ? path.length() : this->length() ); XMP_Int32 matchCount = 0; for( XMP_Int32 i=0; i<depth; i++ ) { const ChunkIdentifier& id1 = this->identifier(i); const ChunkIdentifier& id2 = path.identifier(i); if( id1.id == id2.id ) { if( i == this->length() - 1 && id1.type == kType_NONE ) { matchCount++; } else if( id1.type == id2.type ) { matchCount++; } } else break; } if( matchCount == depth ) { ret = ( path.length() >= this->length() ? kFullMatch : kPartMatch ); } } return ret; }
void ChunkController::parseChunks( XMP_IO* stream, ChunkPath& currentPath, XMP_OptionBits* options /* = NULL */, Chunk* parent /* = NULL */) { XMP_Uns64 filePos = stream->Offset(); XMP_Bool isRoot = (parent == mRoot); XMP_Uns64 parseLimit = mFileSize; XMP_Uns32 chunkCnt = 0; XMP_Validate( mRoot != NULL, "ERROR inserting Chunk. mRoot is NULL.", kXMPErr_InternalFailure ); XMP_Assert(dynamic_cast<Chunk*>(mRoot) == static_cast<Chunk*>(mRoot)); parent = ( parent == NULL ? dynamic_cast<Chunk*>(mRoot) : parent ); // // calculate the parse limit // if ( !isRoot ) { parseLimit = parent->getOriginalOffset() + parent->getSize( true ); if( parseLimit > mFileSize ) { parseLimit = mFileSize; } } while ( filePos < parseLimit ) { XMP_Uns64 fileTail = mFileSize - filePos; // // check if there is enough space (at least for id and size) // if ( fileTail < Chunk::HEADER_SIZE ) { //preserve rest of bytes (fileTail) mTrailingGarbageOffset = filePos; mTrailingGarbageSize = fileTail; break; // stop parsing } else { bool chunkJump = false; // // create a new Chunk // Chunk* chunk = Chunk::createChunk(* mEndian ); bool readFailure = false; // // read the Chunk (id, size, [type]) without caching the data // try { chunk->readChunk( stream ); } catch( ... ) { // remember exception during reading the chunk readFailure = true; } // // validate chunk ID for top-level chunks // if( isRoot && ! mChunkBehavior->isValidTopLevelChunk( chunk->getIdentifier(), chunkCnt ) ) { // notValid: preserve rest of bytes (fileTail) mTrailingGarbageOffset = filePos; mTrailingGarbageSize = fileTail; //delete unused chunk (because these are undefined trailing bytes) delete chunk; break; // stop parsing } else if ( readFailure ) { delete chunk; XMP_Throw ( "Bad RIFF chunk", kXMPErr_BadFileFormat ); } // // parenting // (as early as possible in order to be able to clean up // the tree correctly in the case of an exception) // parent->appendChild(chunk, false); // count top-level chunks if( isRoot ) { chunkCnt++; } // // check size if value exceeds 4GB border // if( chunk->getSize() >= 0x00000000FFFFFFFFLL ) { // remember file position XMP_Int64 currentFilePos = stream->Offset(); // ask for the "real" size value XMP_Uns64 realSize = mChunkBehavior->getRealSize( chunk->getSize(), chunk->getIdentifier(), *mRoot, stream ); // set new size at chunk chunk->setSize( realSize, true ); // set flag if the file position changed chunkJump = currentFilePos < stream->Offset(); } // // Repair if needed // if ( filePos + chunk->getSize(true) > mFileSize ) { bool isUpdate = ( options != NULL ? XMP_OptionIsSet ( *options, kXMPFiles_OpenForUpdate ) : false ); bool repairFile = ( options != NULL ? XMP_OptionIsSet ( *options, kXMPFiles_OpenRepairFile ) : false ); if ( ( ! isUpdate ) || ( repairFile && isRoot ) ) { chunk->setSize( mFileSize-filePos-Chunk::HEADER_SIZE, true ); } else { XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat ); } } // extend search path currentPath.append( chunk->getIdentifier() ); // first 4 bytes might be already read by the chunk->readChunk function XMP_Uns64 offsetOfChunkRead = stream->Offset() - filePos - Chunk::HEADER_SIZE; switch ( compareChunkPaths(currentPath) ) { case ChunkPath::kFullMatch : { chunk->cacheChunkData( stream ); } break; case ChunkPath::kPartMatch : { parseChunks( stream, currentPath, options, chunk); // recalculate the size based on the sizes of its children chunk->calculateSize( true ); } break; case ChunkPath::kNoMatch : { // Not a chunk we are interested in, so mark it as not changed // It will then be ignored by any further logic chunk->resetChanges(); if ( !chunkJump && chunk->getSize() > 0) // if chunk not empty { XMP_Validate( stream->Offset() + chunk->getSize() - offsetOfChunkRead <= mFileSize , "ERROR: want's to skip beyond EOF", kXMPErr_InternalFailure); stream->Seek ( chunk->getSize() - offsetOfChunkRead , kXMP_SeekFromCurrent ); } } break; } // remove last identifier from current path currentPath.remove(); // update current file position filePos = stream->Offset(); // skip pad byte if there is one (if size odd) if( filePos < mFileSize && ( ( chunkJump && ( stream->Offset() & 1 ) > 0 ) || ( !chunkJump && ( chunk->getSize() & 1 ) > 0 ) ) ) { stream->Seek ( 1 , kXMP_SeekFromCurrent ); filePos++; } } } }