summaryrefslogtreecommitdiff
path: root/source/XMPFiles/FormatSupport/RIFF_Support.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/XMPFiles/FormatSupport/RIFF_Support.cpp')
-rw-r--r--source/XMPFiles/FormatSupport/RIFF_Support.cpp491
1 files changed, 491 insertions, 0 deletions
diff --git a/source/XMPFiles/FormatSupport/RIFF_Support.cpp b/source/XMPFiles/FormatSupport/RIFF_Support.cpp
new file mode 100644
index 0000000..3b25568
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/RIFF_Support.cpp
@@ -0,0 +1,491 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002-2007 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "RIFF_Support.hpp"
+
+namespace RIFF_Support {
+
+ #define ckidPremierePadding MakeFourCC ('J','U','N','Q')
+ #define formtypeAVIX MakeFourCC ('A', 'V', 'I', 'X')
+
+
+ #ifndef AVIMAXCHUNKSIZE
+ #define AVIMAXCHUNKSIZE ((UInt32) 0x80000000) /* 2 GB */
+ #endif
+
+
+ typedef struct
+ {
+ long id;
+ UInt32 len;
+ } atag;
+
+ // Local function declarations
+ static bool ReadTag ( LFA_FileRef inFileRef, long * outTag, UInt32 * outLength, long * subtype, UInt64 & inOutPosition );
+ static void AddTag ( RiffState & inOutRiffState, long tag, UInt32 len, UInt64 & inOutPosition, long parentID, long parentnum, long subtypeID );
+ static long SubRead ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long parentid, UInt32 parentlen, UInt64 & inOutPosition );
+ static bool ReadChunk ( LFA_FileRef inFileRef, UInt64 & pos, UInt32 len, char * outBuffer );
+
+ #define GetFilePosition(file) LFA_Seek ( file, 0, SEEK_CUR )
+
+ // =============================================================================================
+
+ bool GetMetaData ( LFA_FileRef inFileRef, long tagID, char * outBuffer, unsigned long * outBufferSize )
+ {
+ RiffState riffState;
+
+ long numTags = OpenRIFF ( inFileRef, riffState );
+ if ( numTags == 0 ) return false;
+
+ return GetRIFFChunk ( inFileRef, riffState, tagID, 0, 0, outBuffer, outBufferSize );
+
+ }
+
+ // =============================================================================================
+
+ bool SetMetaData ( LFA_FileRef inFileRef, long riffType, long tagID, const char * inBuffer, unsigned long inBufferSize )
+ {
+ RiffState riffState;
+
+ long numTags = OpenRIFF ( inFileRef, riffState );
+ if ( numTags == 0 ) return false;
+
+ return PutChunk ( inFileRef, riffState, riffType, tagID, inBuffer, inBufferSize );
+
+ }
+
+ // =============================================================================================
+
+ bool MarkChunkAsPadding ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, long subtypeID )
+ {
+ UInt32 len;
+ UInt64 pos;
+ atag tag;
+
+ try {
+
+ bool found = FindChunk ( inOutRiffState, tagID, riffType, subtypeID, NULL, &len, &pos );
+ if ( ! found ) return false;
+
+ if ( subtypeID != 0 ) {
+ pos -= 12;
+ } else {
+ pos -= 8;
+ }
+
+ tag.id = MakeUns32LE ( ckidPremierePadding );
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+ LFA_Write ( inFileRef, &tag, 4 );
+
+ pos += 8;
+ AddTag ( inOutRiffState, ckidPremierePadding, len, pos, 0, 0, 0 );
+
+ } catch(...) {
+
+ return false; // If a write fails, it throws, so we return false.
+
+ }
+
+ return true;
+ }
+
+ // =============================================================================================
+
+ bool PutChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, const char * inBuffer, UInt32 inBufferSize )
+ {
+ UInt32 len;
+ UInt64 pos;
+ atag tag;
+
+ // Make sure we're writting an even number of bytes. Required by the RIFF specification.
+ XMP_Assert ( (inBufferSize & 1) == 0 );
+
+ try {
+
+ bool found = FindChunk ( inOutRiffState, tagID, 0, 0, NULL, &len, &pos );
+ if ( found ) {
+
+ if ( len == inBufferSize ) {
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+ LFA_Write ( inFileRef, inBuffer, inBufferSize );
+ return true;
+ }
+
+ pos -= 8;
+ tag.id = MakeUns32LE ( ckidPremierePadding );
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+ LFA_Write ( inFileRef, &tag, 4 );
+
+ if ( len > inBufferSize ) {
+ pos += 8;
+ AddTag ( inOutRiffState, ckidPremierePadding, len, pos, 0, 0, 0 );
+ }
+
+ }
+
+ } catch ( ... ) {
+
+ // If a write fails, it throws, so we return false
+ return false;
+
+ }
+
+ bool ok = MakeChunk ( inFileRef, inOutRiffState, riffType, (inBufferSize + 8) );
+ if ( ! ok ) return false;
+
+ return WriteChunk ( inFileRef, tagID, inBuffer, inBufferSize );
+
+ }
+
+ // =============================================================================================
+
+ bool RewriteChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID, long parentID, const char * inData )
+ {
+ UInt32 len;
+ UInt64 pos;
+
+ try {
+ if ( FindChunk ( inOutRiffState, tagID, parentID, 0, NULL, &len, &pos ) ) {
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+ LFA_Write ( inFileRef, inData, len );
+ }
+ } catch ( ... ) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ // =============================================================================================
+
+ bool MakeChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, UInt32 len )
+ {
+ long starttag, taglen;
+ UInt32 rifflen, avail;
+ UInt64 pos;
+
+ /* look for top level Premiere padding chunk */
+ starttag = 0;
+ while ( FindChunk ( inOutRiffState, ckidPremierePadding, riffType, 0, &starttag, reinterpret_cast<unsigned long*>(&taglen), &pos ) ) {
+
+ pos -= 8;
+ taglen += 8;
+ long extra = taglen - len;
+ if ( extra < 0 ) continue;
+
+ RiffIterator iter = inOutRiffState.tags.begin();
+ iter += (starttag - 1);
+
+ if ( extra == 0 ) {
+
+ iter->len = 0;
+
+ } else {
+
+ atag pad;
+ UInt64 padpos;
+
+ /* need 8 bytes extra to be able to split it */
+ extra -= 8;
+ if ( extra < 0 ) continue;
+
+ try{
+ padpos = pos + len;
+ LFA_Seek ( inFileRef, padpos, SEEK_SET );
+ pad.id = MakeUns32LE ( ckidPremierePadding );
+ pad.len = MakeUns32LE ( extra );
+ LFA_Write ( inFileRef, &pad, sizeof(pad) );
+ } catch ( ... ) {
+ return false;
+ }
+
+ iter->pos = padpos + 8;
+ iter->len = extra;
+
+ }
+
+ /* seek back to start of original padding chunk */
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+
+ return true;
+
+ }
+
+ /* can't take padding chunk, so append new chunk to end of file */
+
+ rifflen = inOutRiffState.rifflen + 8;
+ avail = AVIMAXCHUNKSIZE - rifflen;
+
+ LFA_Seek ( inFileRef, 0, SEEK_END );
+ pos = GetFilePosition ( inFileRef );
+
+ if ( avail < len ) {
+
+ /* if needed, create new AVIX chunk */
+ ltag avix;
+
+ avix.id = MakeUns32LE ( FOURCC_RIFF );
+ avix.len = MakeUns32LE ( 4 + len );
+ avix.subid = MakeUns32LE ( formtypeAVIX );
+ LFA_Write(inFileRef, &avix, sizeof(avix));
+
+ pos += 12;
+ AddTag ( inOutRiffState, avix.id, len, pos, 0, 0, 0 );
+
+ } else {
+
+ /* otherwise, rewrite length of last RIFF chunk in file */
+ pos = inOutRiffState.riffpos + 4;
+ rifflen = inOutRiffState.rifflen + len;
+ XMP_Uns32 fileLen = MakeUns32LE ( rifflen );
+ LFA_Seek ( inFileRef, pos, SEEK_SET );
+ LFA_Write ( inFileRef, &fileLen, 4 );
+ inOutRiffState.rifflen = rifflen;
+
+ /* prepare to write data */
+ LFA_Seek ( inFileRef, 0, SEEK_END );
+
+ }
+
+ return true;
+
+ }
+
+ // =============================================================================================
+
+ bool WriteChunk ( LFA_FileRef inFileRef, long tagID, const char * data, UInt32 len )
+ {
+ atag ck;
+ ck.id = MakeUns32LE ( tagID );
+ ck.len = MakeUns32LE ( len );
+
+ try {
+ LFA_Write ( inFileRef, &ck, 8 );
+ LFA_Write ( inFileRef, data, len );
+ } catch ( ... ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // =============================================================================================
+
+ long OpenRIFF ( LFA_FileRef inFileRef, RiffState & inOutRiffState )
+ {
+ UInt64 pos = 0;
+ long tag, subtype;
+ UInt32 len;
+
+ LFA_Seek ( inFileRef, 0, SEEK_SET );
+
+ // read first tag (always RIFFtype)
+ while ( ReadTag ( inFileRef, &tag, &len, &subtype, pos) ) {
+ if ( tag != FOURCC_RIFF ) break;
+ AddTag ( inOutRiffState, tag, len, pos, 0, 0, subtype );
+ if ( subtype != 0 ) SubRead ( inFileRef, inOutRiffState, subtype, len, pos );
+ }
+
+ return inOutRiffState.tags.size();
+
+ }
+
+ // =============================================================================================
+
+ static bool ReadTag ( LFA_FileRef inFileRef, long * outTag, UInt32 * outLength, long * subtype, UInt64 & inOutPosition )
+ {
+ UInt32 realLength;
+
+ try {
+
+ long bytesRead;
+ bytesRead = LFA_Read ( inFileRef, outTag, 4 );
+ if ( bytesRead == 0 ) return false;
+ *outTag = GetUns32LE ( outTag );
+
+ bytesRead = LFA_Read ( inFileRef, outLength, 4 );
+ if ( bytesRead == 0 ) return false;
+ *outLength = GetUns32LE ( outLength );
+
+ realLength = *outLength;
+ realLength += (realLength & 1); // round up to words
+
+ *subtype = 0;
+
+ if ( (*outTag != FOURCC_LIST) && (*outTag != FOURCC_RIFF) ) {
+
+ inOutPosition = GetFilePosition ( inFileRef );
+ UInt64 tempPos = inOutPosition + realLength;
+ LFA_Seek ( inFileRef, tempPos, SEEK_SET );
+
+ } else {
+
+ bytesRead = LFA_Read ( inFileRef, subtype, 4 );
+ if ( bytesRead == 0 ) return false;
+ *subtype = GetUns32LE ( subtype );
+
+ *outLength -= 4;
+ realLength -= 4;
+
+ // Special case:
+ // Since the 'movi' chunk can contain billions of subchunks, skip over the 'movi' subchunk.
+ //
+ // The 'movi' subtype is added to the list as the TAG.
+ // The subtype is returned empty so nobody will try to parse the subchunks.
+
+ if ( *subtype == listtypeAVIMOVIE ) {
+ inOutPosition = GetFilePosition ( inFileRef );
+ UInt64 tempPos = inOutPosition + realLength;
+ LFA_Seek ( inFileRef, tempPos, SEEK_SET );
+ *outLength += 4;
+ *outTag = *subtype;
+ *subtype = 0;
+ }
+
+ inOutPosition = GetFilePosition ( inFileRef );
+
+ }
+
+ } catch ( ... ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ }
+
+ // =============================================================================================
+
+ static void AddTag ( RiffState & inOutRiffState, long tag, UInt32 len, UInt64 & inOutPosition, long parentID, long parentnum, long subtypeID )
+ {
+ RiffTag newTag;
+
+ newTag.pos = inOutPosition;
+ newTag.tagID = tag;
+ newTag.len = len;
+ newTag.parent = parentnum;
+ newTag.parentID = parentID;
+ newTag.subtypeID = subtypeID;
+
+ inOutRiffState.tags.push_back ( newTag );
+
+ if ( tag == FOURCC_RIFF ) {
+ inOutRiffState.riffpos = inOutPosition - 12;
+ inOutRiffState.rifflen = len + 4;
+ }
+
+ }
+
+ // =============================================================================================
+
+ static long SubRead ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long parentid, UInt32 parentlen, UInt64 & inOutPosition )
+ {
+ long tag;
+ long subtype = 0;
+ long parentnum;
+ UInt32 len, total, childlen;
+ UInt64 oldpos;
+
+ total = 0;
+ parentnum = inOutRiffState.tags.size() - 1;
+
+ while ( parentlen > 0 ) {
+
+ oldpos = inOutPosition;
+ ReadTag ( inFileRef, &tag, &len, &subtype, inOutPosition );
+ AddTag ( inOutRiffState, tag, len, inOutPosition, parentid, parentnum, subtype );
+ len += (len & 1);
+
+ if ( subtype == 0 ) {
+ childlen = 8 + len;
+ } else {
+ childlen = 12 + SubRead ( inFileRef, inOutRiffState, subtype, len, inOutPosition );
+ }
+
+ if ( parentlen < childlen ) parentlen = childlen;
+ parentlen -= childlen;
+ total += childlen;
+
+ }
+
+ return total;
+
+ }
+
+ // =============================================================================================
+
+ bool GetRIFFChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID,
+ long parentID, long subtypeID, char * outBuffer, unsigned long * outBufferSize )
+ {
+ UInt32 len;
+ UInt64 pos;
+
+ bool found = FindChunk ( inOutRiffState, tagID, parentID, subtypeID, 0, &len, &pos );
+ if ( ! found ) return false;
+
+ if ( outBuffer == 0 ) {
+ *outBufferSize = (unsigned long)len;
+ return true; // Found, but not wanted.
+ }
+
+ if ( len > *outBufferSize ) len = *outBufferSize;
+
+ found = ReadChunk ( inFileRef, pos, len, outBuffer );
+ return found;
+
+ }
+
+ // =============================================================================================
+
+ bool FindChunk ( RiffState & inOutRiffState, long tagID, long parentID, long subtypeID,
+ long * startTagIndex, UInt32 * len, UInt64 * pos)
+ {
+ std::vector<RiffTag>::iterator iter = inOutRiffState.tags.begin();
+ std::vector<RiffTag>::iterator endIter = inOutRiffState.tags.end();
+
+ // If we're using the next index, skip the iterator.
+ if ( startTagIndex != 0 ) iter += *startTagIndex;
+
+ for ( ; iter != endIter ; ++iter ) {
+
+ if ( startTagIndex != 0 ) *startTagIndex += 1;
+
+ if ( (parentID!= 0) && (iter->parentID != parentID) ) continue;
+ if ( (tagID != 0) && (iter->tagID != tagID) ) continue;
+ if ( (subtypeID != 0) && (iter->subtypeID != subtypeID) ) continue;
+
+ if ( len != 0 ) *len = iter->len;
+ if ( pos != 0 ) *pos = iter->pos;
+
+ return true;
+
+ }
+
+ return false;
+ }
+
+ // =============================================================================================
+
+ static bool ReadChunk ( LFA_FileRef inFileRef, UInt64 & pos, UInt32 len, char * outBuffer )
+ {
+
+ if ( (inFileRef == 0) || (outBuffer == 0) ) return false;
+
+ LFA_Seek (inFileRef, pos, SEEK_SET );
+ UInt32 bytesRead = LFA_Read ( inFileRef, outBuffer, len );
+ if ( bytesRead != len ) return false;
+
+ return true;
+
+ }
+
+} // namespace RIFF_Support