1 // ================================================================================================= 2 // ADOBE SYSTEMS INCORPORATED 3 // Copyright 2010 Adobe Systems Incorporated 4 // All Rights Reserved 5 // 6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms 7 // of the Adobe license agreement accompanying it. 8 // ================================================================================================= 9 10 #ifndef _Chunk_h_ 11 #define _Chunk_h_ 12 13 #include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. 14 15 #include "public/include/XMP_Const.h" 16 #include "public/include/XMP_IO.hpp" 17 18 #include "XMPFiles/source/XMPFiles_Impl.hpp" 19 #include "source/XMPFiles_IO.hpp" 20 21 #include "source/Endian.h" 22 #include "XMPFiles/source/FormatSupport/IFF/ChunkPath.h" 23 #include "XMPFiles/source/FormatSupport/IFF/IChunkData.h" 24 #include "XMPFiles/source/FormatSupport/IFF/IChunkContainer.h" 25 26 namespace IFF_RIFF 27 { 28 /** 29 * CHUNK_UNKNOWN = Either new chunk or a chunk that was read, but not cached 30 * (it is not decided yet whether it becones a node or leaf or is not cached at all) 31 * CHUNK_NODE = Node chunk that contains children, but no own data (except the optional type) 32 * CHUNK_LEAF = Leaf chunk that contains data but no children 33 */ 34 enum ChunkMode { CHUNK_UNKNOWN = 0, CHUNK_NODE = 1, CHUNK_LEAF = 2 }; 35 36 /** 37 * Each Chunk of the IFF/RIFF based file formats (e.g. WAVE, AVI, AIFF) are represented by 38 * instances of the class Chunk. 39 * A chunk can be a node chunk containing children, a leaf chunk containing data or an "unknown" chunk, 40 * which means that its content has not cached/loaded yet (or will never be during the file handling); 41 * see ChunkMode for more details. 42 * 43 * Note: A Chunk can either have a chunk OR a list of child chunks, but never both. 44 * 45 * This class provides access to the children or the data of a chunk, depending of the type. 46 * It keeps track of its size. When the size is changed (by adding or removing data/ or children), 47 * the size is also fixed for the parent hierarchy. 48 * 49 * The dirty flag (hasChanged()) that is set for each change of a chunk is also promoted to the parents. 50 * The chunk stores its original and new offset within the host file, but its *not* automatically correctin the offset; 51 * this is done by the IChunkBehavior class. 52 * The Chunk class provides an interface to iterate through the tree structure of Chunks. 53 * There are methods to insert, remove and move Chunk's in its children tree structure. 54 * 55 * The chunk can read itself from a host file (readChunk()), but it does not automatically read its children, 56 * because they are not necessarily used by the file handler. 57 * The method writeChunk() recurses through the complete chunk tree and writes the *changed* chunks back to the host file. 58 * It is important that the offsets have been fixed before. 59 * 60 * Table about endianess in the different RIFF file formats: 61 * 62 * | ID size type data 63 * ----------------------------------------- 64 * AVI | BE LE BE LE 65 * WAV | BE LE BE LE 66 * AIFF | BE BE BE BE 67 */ 68 class Chunk : public IChunkData, 69 public IChunkContainer 70 { 71 public: 72 /** Factory to create an empty chunk */ 73 static Chunk* createChunk( const IEndian& endian ); 74 75 /** Factory to create an empty chunk */ 76 static Chunk* createUnknownChunk( 77 const IEndian& endian, 78 const XMP_Uns32 id, 79 const XMP_Uns32 type, 80 const XMP_Uns64 size, 81 const XMP_Uns64 originalOffset = 0, 82 const XMP_Uns64 offset = 0 83 ); 84 85 /** Static factory to create a leaf chunk with no data area or only the type in the data area */ 86 static Chunk* createHeaderChunk( const IEndian& endian, const XMP_Uns32 id, const XMP_Uns32 type = kType_NONE ); 87 88 /** 89 * dtor 90 */ 91 ~Chunk(); 92 93 94 //===================== IChunkData interface implementation ================ 95 96 /** 97 * Get the chunk ID 98 * 99 * @return Return the ID, 0 if the chunk does not have an ID. 100 */ getID()101 inline XMP_Uns32 getID() const { return mChunkId.id; } 102 103 /** 104 * Get the chunk type (if available) 105 * (the first four data bytes of the chunk could be a chunk type) 106 * 107 * @return Return the type, kType_NONE if the chunk does not contain data. 108 */ getType()109 inline XMP_Uns32 getType() const { return mChunkId.type; } 110 111 /** 112 * Get the chunk identifier [id and type] 113 * 114 * @return Return the identifier 115 */ getIdentifier()116 inline const ChunkIdentifier& getIdentifier() const { return mChunkId; } 117 118 /** 119 * Access the data of the chunk. 120 * 121 * @param data OUT pointer to the byte array 122 * @return size of the data block, 0 if no data is available 123 */ 124 XMP_Uns64 getData( const XMP_Uns8** data ) const; 125 126 /** 127 * Set new data for the chunk. 128 * Will delete an existing internal buffer and recreate a new one 129 * and copy the given data into that new buffer. 130 * 131 * @param data pointer to the data to put into the chunk 132 * @param size Size of the data block 133 * @param writeType if true, the type of the chunk (getType()) is written in front of the data block. 134 */ 135 void setData( const XMP_Uns8* const data, XMP_Uns64 size, XMP_Bool writeType = false ); 136 137 /** 138 * Returns the current size of the Chunk. 139 * 140 * @param includeHeader if set, the returned size will be the whole chunk size including the header 141 * @return Returns either the size of the data block of the chunk or size of the whole chunk, including the eight byte header. 142 */ 143 XMP_Uns64 getSize( bool includeHeader = false ) const { return includeHeader ? mSize + HEADER_SIZE : mSize; } 144 145 /** 146 * Returns the current size of the Chunk including a pad byte if the size isn't a even number 147 * 148 * @param includeHeader if set, the returned size will be the whole chunk size including the header 149 * @return Returns either the size of the data block of the chunk or size of the whole chunk, including the eight byte header. 150 */ 151 XMP_Uns64 getPadSize( bool includeHeader = false ) const; 152 /** 153 * @return Returns the mode of the chunk (see ChunkMode definition). 154 */ getChunkMode()155 ChunkMode getChunkMode() const { return mChunkMode; } 156 157 /* The following methods are getter/setter for certain data types. 158 * They always take care of little-endian/big-endian issues. 159 * The offset starts at the data area of the Chunk. */ 160 161 XMP_Uns32 getUns32( XMP_Uns64 offset=0 ) const; 162 void setUns32( XMP_Uns32 value, XMP_Uns64 offset=0 ); 163 164 XMP_Uns64 getUns64( XMP_Uns64 offset=0 ) const; 165 void setUns64( XMP_Uns64 value, XMP_Uns64 offset=0 ); 166 167 XMP_Int32 getInt32( XMP_Uns64 offset=0 ) const; 168 void setInt32( XMP_Int32 value, XMP_Uns64 offset=0 ); 169 170 XMP_Int64 getInt64( XMP_Uns64 offset=0 ) const; 171 void setInt64( XMP_Int64 value, XMP_Uns64 offset=0 ); 172 173 std::string getString( XMP_Uns64 size = 0, XMP_Uns64 offset=0 ) const; 174 void setString( std::string value, XMP_Uns64 offset=0 ); 175 176 177 //===================== IChunk interface implementation ================ 178 179 //FIXME XMP exception if size cast from 64 to 32 looses data 180 181 /** 182 * Sets the chunk id. 183 */ 184 void setID( XMP_Uns32 id ); 185 186 /** 187 * Sets the chunk type. 188 */ 189 void setType( XMP_Uns32 type ); 190 191 /** 192 * Sets the chunk size. 193 * NOTE: Should only be used for repairing wrong sizes in files (repair flag). 194 * Normally Size is changed by changing the data automatically! 195 */ 196 inline void setSize( XMP_Uns64 newSize, bool setOriginal = false ) { mDirty = mSize != newSize; mSize = newSize; mOriginalSize = setOriginal ? newSize : mOriginalSize; } 197 198 /** 199 * Calculate the size of the chunks that are dirty including the size 200 * of its children 201 */ 202 XMP_Int64 calculateWriteSize( ) const; 203 204 /** 205 * Calculate the size of this chunks based on its children sizes. 206 * If this chunk has no children then no new size will be calculated. 207 */ 208 XMP_Uns64 calculateSize( bool setOriginal = false ); 209 210 /** 211 * @return Returns the offset of the chunk within the stream. 212 */ getOffset()213 inline XMP_Uns64 getOffset () const { return mOffset; } 214 215 /** 216 * @return Returns the original offset of the chunk within the stream. 217 */ getOriginalOffset()218 inline XMP_Uns64 getOriginalOffset () const { return mOriginalOffset; } 219 220 /** 221 * Returns the original size of the Chunk 222 * 223 * @param includeHeader if set, the returned original size will be the whole chunk size including the header 224 * @return Returns the original size of the chunk within the stream (inluding/excluding headerSize). 225 */ 226 inline XMP_Uns64 getOriginalSize( bool includeHeader = false ) const { return includeHeader ? mOriginalSize + HEADER_SIZE : mOriginalSize; } 227 228 /** 229 * Returns the original size of the Chunk including a pad byte if the size isn't a even number 230 * 231 * @param includeHeader if set, the returned size will be the whole chunk size including the header 232 * @return Returns either the size of the data block of the chunk or size of the whole chunk, including the eight byte header. 233 */ 234 XMP_Uns64 getOriginalPadSize( bool includeHeader = false ) const; 235 236 /** 237 * Adjust the offset that this chunk has within the file. 238 * 239 * @param newOffset the new offset within the file stream 240 */ 241 void setOffset (XMP_Uns64 newOffset); // changes during rearranging 242 243 /** 244 * Has the Chunk class changes, or has the position within the file been changed? 245 * If the result is true the chunk has to be written back to the file 246 * (all parent chunks are also set to dirty in that case). 247 * 248 * @return Returns true if the chunk node has been modified. 249 */ hasChanged()250 XMP_Bool hasChanged() const { return mDirty; } 251 252 /** 253 * Sets this node and all of its parents up to the tree root dirty. 254 */ 255 void setChanged(); 256 257 /** 258 * Resets the dirty status for this chunk and its children to false 259 */ 260 void resetChanges(); 261 262 /** 263 *Sets all necessary member variables to flag this chunk as a new one being inserted into the tree 264 */ 265 void setAsNew(); 266 267 /** 268 * @return Returns the parent chunk (can be NULL if this is the root of the tree). 269 */ getParent()270 inline Chunk* getParent() const { return mParent; } 271 272 /** 273 * Creates a string representation of the chunk (debug method). 274 */ 275 std::string toString( std::string tabs = std::string() , XMP_Bool showOriginal = false ); 276 277 278 //------------------- 279 // file access 280 //------------------- 281 282 /** 283 * Read id, size and offset and create a chunk with mode CHUNK_UNKNOWN. 284 * The file is expected to be open and is not closed! 285 * 286 * @param file File reference to read the chunk from 287 */ 288 void readChunk( XMP_IO* file ); 289 290 /** 291 * Stores the data in the class (only called if required). 292 * The file is expected to be open and is not closed! 293 * 294 * @param file File reference to cache the chunk data 295 */ 296 void cacheChunkData( XMP_IO* file ); 297 298 /** 299 * Write or updates chunk (new data, new size, new position). 300 * The file is expected to be open and is not closed! 301 * 302 * Behavior for the different chunk types: 303 * 304 * CHUNK_UNKNOWN: 305 * - does not write anything back 306 * - throws exception if hasChanged == true 307 * 308 * CHUNK_LEAF: 309 * - writes ID (starting with offset) 310 * - writes size 311 * - writes buffer (including the optional type at the beginning) 312 * 313 * CHUNK_NODE: 314 * - writes ID (starting with offset) 315 * - writes size 316 * - writes type if defined 317 * - calls writeChunk on it's children 318 * 319 * Note: readChunk() and optionally cacheChunkData() has to be called before! 320 * 321 * @param file File reference to write the chunk to 322 */ 323 void writeChunk( XMP_IO* file ); 324 325 326 //------------------- 327 // children access 328 //------------------- 329 330 /** 331 * @return Returns the number children chunks. 332 */ 333 XMP_Uns32 numChildren() const; 334 335 /** 336 * Returns a child node. 337 * 338 * @param pos position of the child node to return 339 * @return Returns the child node at the given position. 340 */ 341 Chunk* getChildAt( XMP_Uns32 pos ) const; 342 343 /** 344 * Appends a child node at the end of the children list. 345 * 346 * @param node the new node 347 * @param adjustSizes adjust size of chunk and parents 348 * @return Returns the added node. 349 */ 350 void appendChild( Chunk* node, XMP_Bool adjustSizes = true ); 351 352 /** 353 * Inserts a child node at a certain position. 354 * 355 * @param pos position in the children list to add the new node 356 * @param node the new node 357 * @return Returns the added node. 358 */ 359 void insertChildAt( XMP_Uns32 pos, Chunk* node ); 360 361 /** 362 * Removes a child node at a given position. 363 * 364 * @param pos position of the node to delete in the children list 365 * 366 * @return The removed chunk 367 */ 368 Chunk* removeChildAt( XMP_Uns32 pos ); 369 370 /** 371 * Remove child at the passed position and insert the new chunk 372 * 373 * @param pos Position of chunk that will be replaced 374 * @param chunk New chunk 375 * 376 * @return Replaced chunk 377 */ 378 Chunk* replaceChildAt( XMP_Uns32 pos, Chunk* node ); 379 380 //-------------------- 381 // children iteration 382 //-------------------- 383 384 typedef std::vector<Chunk*>::iterator ChunkIterator; 385 typedef std::vector<Chunk*>::const_iterator ConstChunkIterator; 386 387 ConstChunkIterator firstChild() const; 388 389 ConstChunkIterator lastChild() const; 390 391 /** The size of the header (id+size) */ 392 static const XMP_Uns8 HEADER_SIZE = 8; 393 /** The size of the type */ 394 static const XMP_Uns8 TYPE_SIZE = 4; 395 396 397 private: 398 /** stores the chunk header */ 399 ChunkIdentifier mChunkId; 400 /** Original size of chunk without the header */ 401 XMP_Uns64 mOriginalSize; 402 /** size of chunk without the header */ 403 XMP_Uns64 mSize; 404 /** size of the internal buffer */ 405 XMP_Uns64 mBufferSize; 406 /** buffer for the chunk data without the header, but including the type (first 4 bytes). */ 407 XMP_Uns8* mData; 408 /** Buffer to hold the first 4 bytes that are used for either the type or as data. 409 * Only used for ReadChunk and CacheChunk */ 410 ChunkMode mChunkMode; 411 412 /** 413 * Current position in stream (file). Can only be changed by moving the chunks around 414 * (by using an IChunkBehavior class). 415 * Note: Sizes are stored in chunk because it can be changed by the handler (i.e. by changing the data) 416 */ 417 XMP_Uns64 mOriginalOffset; 418 /** 419 * New position of the chunk in the stream. 420 * It is initialized to MAXINT64 when there is no new offset. 421 * If the offset has been changed the dirty flag has to be set. 422 */ 423 XMP_Uns64 mOffset; 424 425 /** 426 * The dirty flag indicates that the chunk (and all parent chunks) has been modified or moved and 427 * that it therefore needs to be written to file. 428 */ 429 XMP_Bool mDirty; // has Chunk data changed? has Chunk position changed? 430 431 /** The parent of this node; only the root node does not have a parent. */ 432 Chunk* mParent; 433 434 /** Stores the byte order for this node. 435 * Note: The endianess does not change within one file */ 436 const IEndian& mEndian; 437 438 /** The list of child nodes. */ 439 std::vector<Chunk*> mChildren; 440 441 /** 442 * private ctor, prevents direct invokation. 443 * 444 * @param endian Endian util 445 */ 446 Chunk( const IEndian& endian ); 447 448 /** 449 * Resizes the internal byte buffer to the given size if the new size is bigger than the current one. 450 * If the new size is smaller, the buffer is not adjusted 451 */ 452 void adjustInternalBuffer( XMP_Uns64 newSize ); 453 454 /** 455 * Adjusts the chunk size and the parents chunk sizes. 456 * - Leaf chunks always have the size of their data, inluding the 4-byte type and excluding the header. 457 * Leaf chunks can have an ODD size! 458 * - Node chunks have the added size of all of their children, including the childrens header, but excluding it's own header. 459 * IMPORTANT: When a leaf child node has an ODD size of data, 460 * a pad byte is added during the writing process and the parent's size INCLUDES the pad byte. 461 */ 462 void adjustSize( XMP_Int64 sizeChange = 0 ); 463 464 }; // Chunk 465 466 } // namespace 467 468 #endif 469