diff options
Diffstat (limited to 'XMPFiles/source/FormatSupport/ASF_Support.cpp')
-rw-r--r-- | XMPFiles/source/FormatSupport/ASF_Support.cpp | 1438 |
1 files changed, 1438 insertions, 0 deletions
diff --git a/XMPFiles/source/FormatSupport/ASF_Support.cpp b/XMPFiles/source/FormatSupport/ASF_Support.cpp new file mode 100644 index 0000000..35adce6 --- /dev/null +++ b/XMPFiles/source/FormatSupport/ASF_Support.cpp @@ -0,0 +1,1438 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006 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 "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Const.h" + +#include "XMPFiles/source/FormatSupport/ASF_Support.hpp" +#include "source/UnicodeConversions.hpp" +#include "source/XIO.hpp" + +#if XMP_WinBuild + #define snprintf _snprintf + #pragma warning ( disable : 4996 ) // '...' was declared deprecated + #pragma warning ( disable : 4267 ) // *** conversion (from size_t), possible loss of date (many 64 bit related) +#endif + +// ============================================================================================= + +// Platforms other than Win +#if ! XMP_WinBuild +int IsEqualGUID ( const GUID& guid1, const GUID& guid2 ) +{ + return (memcmp ( &guid1, &guid2, sizeof(GUID) ) == 0); +} +#endif + +ASF_Support::ASF_Support() : legacyManager(0), posFileSizeInfo(0) {} + +ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager ) : posFileSizeInfo(0) +{ + legacyManager = _legacyManager; +} + +ASF_Support::~ASF_Support() +{ + legacyManager = 0; +} + +// ============================================================================================= + +long ASF_Support::OpenASF ( XMP_IO* fileRef, ObjectState & inOutObjectState ) +{ + XMP_Uns64 pos = 0; + XMP_Uns64 len; + + try { + pos = fileRef->Rewind(); + } catch ( ... ) {} + + if ( pos != 0 ) return 0; + + // read first and following chunks + while ( ReadObject ( fileRef, inOutObjectState, &len, pos) ) {} + + return inOutObjectState.objects.size(); + +} + +// ============================================================================================= + +bool ASF_Support::ReadObject ( XMP_IO* fileRef, ObjectState & inOutObjectState, XMP_Uns64 * objectLength, XMP_Uns64 & inOutPosition ) +{ + + try { + + XMP_Uns64 startPosition = inOutPosition; + XMP_Uns32 bytesRead; + ASF_ObjectBase objectBase; + + bytesRead = fileRef->ReadAll ( &objectBase, kASF_ObjectBaseLen ); + if ( bytesRead != kASF_ObjectBaseLen ) return false; + + *objectLength = GetUns64LE ( &objectBase.size ); + inOutPosition += *objectLength; + + ObjectData newObject; + + newObject.pos = startPosition; + newObject.len = *objectLength; + newObject.guid = objectBase.guid; + + // xmpIsLastObject indicates, that the XMP-object is the last top-level object + // reset here, if any another object is read + inOutObjectState.xmpIsLastObject = false; + + if ( IsEqualGUID ( ASF_Header_Object, newObject.guid ) ) { + + // header object ? + this->ReadHeaderObject ( fileRef, inOutObjectState, newObject ); + + } else if ( IsEqualGUID ( ASF_XMP_Metadata, newObject.guid ) ) { + + // check object for XMP GUID + inOutObjectState.xmpPos = newObject.pos + kASF_ObjectBaseLen; + inOutObjectState.xmpLen = newObject.len - kASF_ObjectBaseLen; + inOutObjectState.xmpIsLastObject = true; + inOutObjectState.xmpObject = newObject; + newObject.xmp = true; + + } + + inOutObjectState.objects.push_back ( newObject ); + + fileRef->Seek ( inOutPosition, kXMP_SeekFromStart ); + + } catch ( ... ) { + + return false; + + } + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::ReadHeaderObject ( XMP_IO* fileRef, ObjectState& inOutObjectState, const ObjectData& newObject ) +{ + if ( ! IsEqualGUID ( ASF_Header_Object, newObject.guid) || (! legacyManager ) ) return false; + + std::string buffer; + + legacyManager->SetPadding(0); + + try { + + // read header-object structure + XMP_Uns64 pos = newObject.pos; + XMP_Uns32 bufferSize = kASF_ObjectBaseLen + 6; + + buffer.clear(); + buffer.reserve ( bufferSize ); + buffer.assign ( bufferSize, ' ' ); + fileRef->Seek ( pos, kXMP_SeekFromStart ); + fileRef->ReadAll ( const_cast<char*>(buffer.data()), bufferSize ); + + XMP_Uns64 read = bufferSize; + pos += bufferSize; + + // read contained header objects + XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); + ASF_ObjectBase objectBase; + + while ( read < newObject.len ) { + + fileRef->Seek ( pos, kXMP_SeekFromStart ); + if ( kASF_ObjectBaseLen != fileRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break; + + fileRef->Seek ( pos, kXMP_SeekFromStart ); + objectBase.size = GetUns64LE ( &objectBase.size ); + + if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid) && (objectBase.size >= 104 ) ) { + + buffer.clear(); + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + fileRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + // save position of filesize-information + posFileSizeInfo = (pos + 40); + + // creation date + std::string sub ( buffer.substr ( 48, 8 ) ); + legacyManager->SetField ( ASF_LegacyManager::fieldCreationDate, sub ); + + // broadcast flag set ? + XMP_Uns32 flags = GetUns32LE ( &buffer[88] ); + inOutObjectState.broadcast = (flags & 1); + legacyManager->SetBroadcast ( inOutObjectState.broadcast ); + + legacyManager->SetObjectExists ( ASF_LegacyManager::objectFileProperties ); + + } else if ( IsEqualGUID ( ASF_Content_Description_Object, objectBase.guid) && (objectBase.size >= 34 ) ) { + + buffer.clear(); + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + fileRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + XMP_Uns16 titleLen = GetUns16LE ( &buffer[24] ); + XMP_Uns16 authorLen = GetUns16LE ( &buffer[26] ); + XMP_Uns16 copyrightLen = GetUns16LE ( &buffer[28] ); + XMP_Uns16 descriptionLen = GetUns16LE ( &buffer[30] ); + XMP_Uns16 ratingLen = GetUns16LE ( &buffer[32] ); + + XMP_Uns16 fieldPos = 34; + + std::string titleStr = buffer.substr ( fieldPos, titleLen ); + fieldPos += titleLen; + legacyManager->SetField ( ASF_LegacyManager::fieldTitle, titleStr ); + + std::string authorStr = buffer.substr ( fieldPos, authorLen ); + fieldPos += authorLen; + legacyManager->SetField ( ASF_LegacyManager::fieldAuthor, authorStr ); + + std::string copyrightStr = buffer.substr ( fieldPos, copyrightLen ); + fieldPos += copyrightLen; + legacyManager->SetField ( ASF_LegacyManager::fieldCopyright, copyrightStr ); + + std::string descriptionStr = buffer.substr ( fieldPos, descriptionLen ); + fieldPos += descriptionLen; + legacyManager->SetField ( ASF_LegacyManager::fieldDescription, descriptionStr ); + + /* rating is currently not part of reconciliation + std::string ratingStr = buffer.substr ( fieldPos, ratingLen ); + fieldPos += ratingLen; + legacyData.append ( titleStr ); + */ + + legacyManager->SetObjectExists ( ASF_LegacyManager::objectContentDescription ); + + } else if ( IsEqualGUID ( ASF_Content_Branding_Object, objectBase.guid ) ) { + + buffer.clear(); + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + fileRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + XMP_Uns32 fieldPos = 28; + + // copyright URL is 3. element with variable size + for ( int i = 1; i <= 3 ; ++i ) { + XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] ); + if ( i == 3 ) { + std::string copyrightURLStr = buffer.substr ( fieldPos + 4, len ); + legacyManager->SetField ( ASF_LegacyManager::fieldCopyrightURL, copyrightURLStr ); + } + fieldPos += (len + 4); + } + + legacyManager->SetObjectExists ( ASF_LegacyManager::objectContentBranding ); + +#if ! Exclude_LicenseURL_Recon + + } else if ( IsEqualGUID ( ASF_Content_Encryption_Object, objectBase.guid ) ) { + + buffer.clear(); + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + fileRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + XMP_Uns32 fieldPos = 24; + + // license URL is 4. element with variable size + for ( int i = 1; i <= 4 ; ++i ) { + XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] ); + if ( i == 4 ) { + std::string licenseURLStr = buffer.substr ( fieldPos + 4, len ); + legacyManager->SetField ( ASF_LegacyManager::fieldLicenseURL, licenseURLStr ); + } + fieldPos += (len + 4); + } + + legacyManager->SetObjectExists ( objectContentEncryption ); + +#endif + + } else if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { + + legacyManager->SetPadding ( legacyManager->GetPadding() + (objectBase.size - 24) ); + + } else if ( IsEqualGUID ( ASF_Header_Extension_Object, objectBase.guid ) ) { + + this->ReadHeaderExtensionObject ( fileRef, inOutObjectState, pos, objectBase ); + + } + + pos += objectBase.size; + read += objectBase.size; + } + + } catch ( ... ) { + + return false; + + } + + legacyManager->ComputeDigest(); + + return true; +} + +// ============================================================================================= + +bool ASF_Support::WriteHeaderObject ( XMP_IO* sourceRef, XMP_IO* destRef, const ObjectData& object, ASF_LegacyManager& _legacyManager, bool usePadding ) +{ + if ( ! IsEqualGUID ( ASF_Header_Object, object.guid ) ) return false; + + std::string buffer; + XMP_Uns16 valueUns16LE; + XMP_Uns32 valueUns32LE; + XMP_Uns64 valueUns64LE; + + try { + + // read header-object structure + XMP_Uns64 pos = object.pos; + XMP_Uns32 bufferSize = kASF_ObjectBaseLen + 6; + + buffer.clear(); + buffer.reserve ( bufferSize ); + buffer.assign ( bufferSize, ' ' ); + sourceRef->Seek ( pos, kXMP_SeekFromStart ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), bufferSize ); + + XMP_Uns64 read = bufferSize; + pos += bufferSize; + + // read contained header objects + XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); + ASF_ObjectBase objectBase; + + // prepare new header in memory + std::string header; + + int changedObjects = _legacyManager.changedObjects(); + int exportedObjects = 0; + int writtenObjects = 0; + + header.append ( buffer.c_str(), bufferSize ); + + while ( read < object.len ) { + + sourceRef->Seek ( pos, kXMP_SeekFromStart ); + if ( kASF_ObjectBaseLen != sourceRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break; + + sourceRef->Seek ( pos, kXMP_SeekFromStart ); + objectBase.size = GetUns64LE ( &objectBase.size ); + + int headerStartPos = header.size(); + + // save position of filesize-information + if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid ) ) { + posFileSizeInfo = (headerStartPos + 40); + } + + // write objects + if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid ) && + (objectBase.size >= 104) && (changedObjects & ASF_LegacyManager::objectFileProperties) ) { + + // copy object and replace creation-date + buffer.reserve ( XMP_Uns32 ( objectBase.size ) ); + buffer.assign ( XMP_Uns32 ( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + header.append ( buffer, 0, XMP_Uns32( objectBase.size ) ); + + if ( ! _legacyManager.GetBroadcast() ) { + buffer = _legacyManager.GetField ( ASF_LegacyManager::fieldCreationDate ); + ReplaceString ( header, buffer, (headerStartPos + 48), 8 ); + } + + exportedObjects |= ASF_LegacyManager::objectFileProperties; + + } else if ( IsEqualGUID ( ASF_Content_Description_Object, objectBase.guid ) && + (objectBase.size >= 34) && (changedObjects & ASF_LegacyManager::objectContentDescription) ) { + + // re-create object with xmp-data + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + // write header only + header.append ( buffer, 0, XMP_Uns32( kASF_ObjectBaseLen ) ); + + // write length fields + + XMP_Uns16 titleLen = _legacyManager.GetField ( ASF_LegacyManager::fieldTitle).size( ); + valueUns16LE = MakeUns16LE ( titleLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 authorLen = _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor).size( ); + valueUns16LE = MakeUns16LE ( authorLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 copyrightLen = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright).size( ); + valueUns16LE = MakeUns16LE ( copyrightLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 descriptionLen = _legacyManager.GetField ( ASF_LegacyManager::fieldDescription).size( ); + valueUns16LE = MakeUns16LE ( descriptionLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + // retrieve existing overall length of preceding fields + XMP_Uns16 precedingLen = 0; + precedingLen += GetUns16LE ( &buffer[24] ); // Title + precedingLen += GetUns16LE ( &buffer[26] ); // Author + precedingLen += GetUns16LE ( &buffer[28] ); // Copyright + precedingLen += GetUns16LE ( &buffer[30] ); // Description + // retrieve existing 'Rating' length + XMP_Uns16 ratingLen = GetUns16LE ( &buffer[32] ); // Rating + valueUns16LE = MakeUns16LE ( ratingLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + // write field contents + + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldTitle ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldDescription ) ); + header.append ( buffer, (34 + precedingLen), ratingLen ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + exportedObjects |= ASF_LegacyManager::objectContentDescription; + + } else if ( IsEqualGUID ( ASF_Content_Branding_Object, objectBase.guid ) && + (changedObjects & ASF_LegacyManager::objectContentBranding) ) { + + // re-create object with xmp-data + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + // calculate size of fields coming before 'Copyright URL' + XMP_Uns32 length = 28; + length += (GetUns32LE ( &buffer[length] ) + 4); // Banner Image Data + length += (GetUns32LE ( &buffer[length] ) + 4); // Banner Image URL + + // write first part of header + header.append ( buffer, 0, length ); + + // copyright URL + length = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL).size( ); + valueUns32LE = MakeUns32LE ( length ); + header.append ( (const char*)&valueUns32LE, 4 ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL ) ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + exportedObjects |= ASF_LegacyManager::objectContentBranding; + +#if ! Exclude_LicenseURL_Recon + + } else if ( IsEqualGUID ( ASF_Content_Encryption_Object, objectBase.guid ) && + (changedObjects & ASF_LegacyManager::objectContentEncryption) ) { + + // re-create object with xmp-data + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + // calculate size of fields coming before 'License URL' + XMP_Uns32 length = 24; + length += (GetUns32LE ( &buffer[length] ) + 4); // Secret Data + length += (GetUns32LE ( &buffer[length] ) + 4); // Protection Type + length += (GetUns32LE ( &buffer[length] ) + 4); // Key ID + + // write first part of header + header.append ( buffer, 0, length ); + + // License URL + length = _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL).size( ); + valueUns32LE = MakeUns32LE ( length ); + header.append ( (const char*)&valueUns32LE, 4 ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL ) ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + exportedObjects |= ASF_LegacyManager::objectContentEncryption; + +#endif + + } else if ( IsEqualGUID ( ASF_Header_Extension_Object, objectBase.guid ) && usePadding ) { + + // re-create object if padding needs to be used + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + ASF_Support::WriteHeaderExtensionObject ( buffer, &header, objectBase, 0 ); + + } else if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) && usePadding ) { + + // eliminate padding (will be created as last object) + + } else { + + // simply copy all other objects + buffer.reserve ( XMP_Uns32( objectBase.size ) ); + buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); + sourceRef->ReadAll ( const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size) ); + + header.append ( buffer, 0, XMP_Uns32( objectBase.size ) ); + + } + + pos += objectBase.size; + read += objectBase.size; + + writtenObjects ++; + + } + + // any objects to create ? + int newObjects = (changedObjects ^ exportedObjects); + + if ( newObjects ) { + + // create new objects with xmp-data + int headerStartPos; + ASF_ObjectBase newObjectBase; + XMP_Uns32 length; + + if ( newObjects & ASF_LegacyManager::objectContentDescription ) { + + headerStartPos = header.size(); + newObjectBase.guid = ASF_Content_Description_Object; + newObjectBase.size = 0; + + // write object header + header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); + + XMP_Uns16 titleLen = _legacyManager.GetField ( ASF_LegacyManager::fieldTitle).size( ); + valueUns16LE = MakeUns16LE ( titleLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 authorLen = _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor).size( ); + valueUns16LE = MakeUns16LE ( authorLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 copyrightLen = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright).size( ); + valueUns16LE = MakeUns16LE ( copyrightLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 descriptionLen = _legacyManager.GetField ( ASF_LegacyManager::fieldDescription).size( ); + valueUns16LE = MakeUns16LE ( descriptionLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + XMP_Uns16 ratingLen = 0; + valueUns16LE = MakeUns16LE ( ratingLen ); + header.append ( (const char*)&valueUns16LE, 2 ); + + // write field contents + + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldTitle ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright ) ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldDescription ) ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + newObjects &= ~ASF_LegacyManager::objectContentDescription; + + writtenObjects ++; + + } + + if ( newObjects & ASF_LegacyManager::objectContentBranding ) { + + headerStartPos = header.size(); + newObjectBase.guid = ASF_Content_Branding_Object; + newObjectBase.size = 0; + + // write object header + header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); + + // write 'empty' fields + header.append ( 12, '\0' ); + + // copyright URL + length = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL).size( ); + valueUns32LE = MakeUns32LE ( length ); + header.append ( (const char*)&valueUns32LE, 4 ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL ) ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + newObjects &= ~ASF_LegacyManager::objectContentBranding; + + writtenObjects ++; + + } + +#if ! Exclude_LicenseURL_Recon + + if ( newObjects & ASF_LegacyManager::objectContentEncryption ) { + + headerStartPos = header.size(); + newObjectBase.guid = ASF_Content_Encryption_Object; + newObjectBase.size = 0; + + // write object header + header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); + + // write 'empty' fields + header.append ( 12, '\0' ); + + // License URL + length = _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL).size( ); + valueUns32LE = MakeUns32LE ( length ); + header.append ( (const char*)&valueUns32LE, 4 ); + header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL ) ); + + // update new object size + valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); + std::string newSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); + + newObjects &= ~ASF_LegacyManager::objectContentEncryption; + + writtenObjects ++; + + } + +#endif + + } + + // create padding object ? + if ( usePadding && (header.size ( ) < object.len ) ) { + ASF_Support::CreatePaddingObject ( &header, (object.len - header.size()) ); + writtenObjects ++; + } + + // update new header-object size + valueUns64LE = MakeUns64LE ( header.size() ); + std::string newValue ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( header, newValue, 16, 8 ); + + // update new number of Header objects + valueUns32LE = MakeUns32LE ( writtenObjects ); + newValue = std::string ( (const char*)&valueUns32LE, 4 ); + ReplaceString ( header, newValue, 24, 4 ); + + // if we are operating on the same file (in-place update), place pointer before writing + if ( sourceRef == destRef ) destRef->Seek ( object.pos, kXMP_SeekFromStart ); + + // write header + destRef->Write ( header.c_str(), header.size() ); + + } catch ( ... ) { + + return false; + + } + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::UpdateHeaderObject ( XMP_IO* fileRef, const ObjectData& object, ASF_LegacyManager& _legacyManager ) +{ + return ASF_Support::WriteHeaderObject ( fileRef, fileRef, object, _legacyManager, true ); +} + +// ============================================================================================= + +bool ASF_Support::UpdateFileSize ( XMP_IO* fileRef ) +{ + if ( fileRef == 0 ) return false; + + XMP_Uns64 posCurrent = fileRef->Seek ( 0, kXMP_SeekFromCurrent ); + XMP_Uns64 newSizeLE = MakeUns64LE ( fileRef->Length() ); + + if ( this->posFileSizeInfo != 0 ) { + + fileRef->Seek ( this->posFileSizeInfo, kXMP_SeekFromStart ); + + } else { + + // The position of the file size field is not known, find it. + + ASF_ObjectBase objHeader; + + // Read the Header object at the start of the file. + + fileRef->Rewind(); + fileRef->ReadAll ( &objHeader, kASF_ObjectBaseLen ); + if ( ! IsEqualGUID ( ASF_Header_Object, objHeader.guid ) ) return false; + + XMP_Uns32 childCount; + fileRef->ReadAll ( &childCount, 4 ); + childCount = GetUns32LE ( &childCount ); + + fileRef->Seek ( 2, kXMP_SeekFromCurrent ); // Skip the 2 reserved bytes. + + // Look for the File Properties object in the Header's children. + + for ( ; childCount > 0; --childCount ) { + fileRef->ReadAll ( &objHeader, kASF_ObjectBaseLen ); + if ( IsEqualGUID ( ASF_File_Properties_Object, objHeader.guid ) ) break; + XMP_Uns64 dataLen = GetUns64LE ( &objHeader.size ) - 24; + fileRef->Seek ( dataLen, kXMP_SeekFromCurrent ); // Skip this object's data. + } + if ( childCount == 0 ) return false; + + // Seek to the file size field. + + XMP_Uns64 fpoSize = GetUns64LE ( &objHeader.size ); + if ( fpoSize < (16+8+16+8) ) return false; + fileRef->Seek ( 16, kXMP_SeekFromCurrent ); // Skip to the file size field. + + } + + fileRef->Write ( &newSizeLE, 8 ); // Write the new file size. + + fileRef->Seek ( posCurrent, kXMP_SeekFromStart ); + return true; + +} + +// ============================================================================================= + +bool ASF_Support::ReadHeaderExtensionObject ( XMP_IO* fileRef, ObjectState& inOutObjectState, const XMP_Uns64& _pos, const ASF_ObjectBase& _objectBase ) +{ + if ( ! IsEqualGUID ( ASF_Header_Extension_Object, _objectBase.guid) || (! legacyManager ) ) return false; + + try { + + // read extended header-object structure beginning at the data part (offset = 46) + const XMP_Uns64 offset = 46; + XMP_Uns64 read = 0; + XMP_Uns64 data = (_objectBase.size - offset); + XMP_Uns64 pos = (_pos + offset); + + ASF_ObjectBase objectBase; + + while ( read < data ) { + + fileRef->Seek ( pos, kXMP_SeekFromStart ); + if ( kASF_ObjectBaseLen != fileRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break; + + objectBase.size = GetUns64LE ( &objectBase.size ); + + if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { + legacyManager->SetPadding ( legacyManager->GetPadding() + (objectBase.size - 24) ); + } + + pos += objectBase.size; + read += objectBase.size; + + } + + } catch ( ... ) { + + return false; + + } + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::WriteHeaderExtensionObject ( const std::string& buffer, std::string* header, const ASF_ObjectBase& _objectBase, const int /*reservePadding*/ ) +{ + if ( ! IsEqualGUID ( ASF_Header_Extension_Object, _objectBase.guid ) || (! header) || (buffer.size() < 46) ) return false; + + const XMP_Uns64 offset = 46; + int startPos = header->size(); + + // copy header base + header->append ( buffer, 0, offset ); + + // read extended header-object structure beginning at the data part (offset = 46) + XMP_Uns64 read = 0; + XMP_Uns64 data = (_objectBase.size - offset); + XMP_Uns64 pos = offset; + + ASF_ObjectBase objectBase; + + while ( read < data ) { + + memcpy ( &objectBase, &buffer[int(pos)], kASF_ObjectBaseLen ); + objectBase.size = GetUns64LE ( &objectBase.size ); + + if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { + // eliminate + } else { + // copy other objects + header->append ( buffer, XMP_Uns32(pos), XMP_Uns32(objectBase.size) ); + } + + pos += objectBase.size; + read += objectBase.size; + + } + + // update header extension data size + XMP_Uns32 valueUns32LE = MakeUns32LE ( header->size() - startPos - offset ); + std::string newDataSize ( (const char*)&valueUns32LE, 4 ); + ReplaceString ( *header, newDataSize, (startPos + 42), 4 ); + + // update new object size + XMP_Uns64 valueUns64LE = MakeUns64LE ( header->size() - startPos ); + std::string newObjectSize ( (const char*)&valueUns64LE, 8 ); + ReplaceString ( *header, newObjectSize, (startPos + 16), 8 ); + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::CreatePaddingObject ( std::string* header, const XMP_Uns64 size ) +{ + if ( ( ! header) || (size < 24) ) return false; + + ASF_ObjectBase newObjectBase; + + newObjectBase.guid = ASF_Padding_Object; + newObjectBase.size = MakeUns64LE ( size ); + + // write object header + header->append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); + + // write 'empty' padding + header->append ( XMP_Uns32 ( size - 24 ), '\0' ); + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::WriteXMPObject ( XMP_IO* fileRef, XMP_Uns32 len, const char* inBuffer ) +{ + bool ret = false; + + ASF_ObjectBase objectBase = { ASF_XMP_Metadata, 0 }; + objectBase.size = MakeUns64LE ( len + kASF_ObjectBaseLen ); + + try { + fileRef->Write ( &objectBase, kASF_ObjectBaseLen ); + fileRef->Write ( inBuffer, len ); + ret = true; + } catch ( ... ) {} + + return ret; + +} + +// ============================================================================================= + +bool ASF_Support::UpdateXMPObject ( XMP_IO* fileRef, const ObjectData& object, XMP_Uns32 len, const char * inBuffer ) +{ + bool ret = false; + + ASF_ObjectBase objectBase = { ASF_XMP_Metadata, 0 }; + objectBase.size = MakeUns64LE ( len + kASF_ObjectBaseLen ); + + try { + fileRef->Seek ( object.pos, kXMP_SeekFromStart ); + fileRef->Write ( &objectBase, kASF_ObjectBaseLen ); + fileRef->Write ( inBuffer, len ); + ret = true; + } catch ( ... ) {} + + return ret; + +} + +// ============================================================================================= + +bool ASF_Support::CopyObject ( XMP_IO* sourceRef, XMP_IO* destRef, const ObjectData& object ) +{ + try { + sourceRef->Seek ( object.pos, kXMP_SeekFromStart ); + XIO::Copy ( sourceRef, destRef, object.len ); + } catch ( ... ) { + return false; + } + + return true; + +} + +// ============================================================================================= + +bool ASF_Support::ReadBuffer ( XMP_IO* fileRef, XMP_Uns64 & pos, XMP_Uns64 len, char * outBuffer ) +{ + try { + + if ( (fileRef == 0) || (outBuffer == 0) ) return false; + + fileRef->Seek ( pos, kXMP_SeekFromStart ); + long bytesRead = fileRef->ReadAll ( outBuffer, XMP_Int32(len) ); + if ( XMP_Uns32 ( bytesRead ) != len ) return false; + + return true; + + } catch ( ... ) {} + + return false; + +} + +// ============================================================================================= + +bool ASF_Support::WriteBuffer ( XMP_IO* fileRef, XMP_Uns64 & pos, XMP_Uns32 len, const char * inBuffer ) +{ + try { + + if ( (fileRef == 0) || (inBuffer == 0) ) return false; + + fileRef->Seek ( pos, kXMP_SeekFromStart ); + fileRef->Write ( inBuffer, len ); + + return true; + + } catch ( ... ) {} + + return false; + +} + +// ================================================================================================= + +std::string ASF_Support::ReplaceString ( std::string& operand, std::string& str, int offset, int count ) +{ + std::basic_string<char>::iterator iterF1, iterL1, iterF2, iterL2; + + iterF1 = operand.begin() + offset; + iterL1 = operand.begin() + offset + count; + iterF2 = str.begin(); + iterL2 = str.begin() + count; + + return operand.replace ( iterF1, iterL1, iterF2, iterL2 ); + +} + +// ================================================================================================= + +ASF_LegacyManager::ASF_LegacyManager() : fields(fieldLast), broadcastSet(false), digestComputed(false), + imported(false), objectsExisting(0), objectsToExport(0), legacyDiff(0), padding(0) +{ + // Nothing more to do. +} + +// ================================================================================================= + +ASF_LegacyManager::~ASF_LegacyManager() +{ + // Nothing to do. +} + +// ================================================================================================= + +bool ASF_LegacyManager::SetField ( fieldType field, const std::string& value ) +{ + if ( field >= fieldLast ) return false; + + unsigned int maxSize = this->GetFieldMaxSize ( field ); + + if (value.size ( ) <= maxSize ) { + fields[field] = value; + } else { + fields[field] = value.substr ( 0, maxSize ); + } + + if ( field == fieldCopyrightURL ) NormalizeStringDisplayASCII ( fields[field] ); + + #if ! Exclude_LicenseURL_Recon + if ( field == fieldLicenseURL ) NormalizeStringDisplayASCII ( fields[field] ); + #endif + + return true; + +} + +// ================================================================================================= + +std::string ASF_LegacyManager::GetField ( fieldType field ) +{ + if ( field >= fieldLast ) return std::string(); + return fields[field]; +} + +// ================================================================================================= + +unsigned int ASF_LegacyManager::GetFieldMaxSize ( fieldType field ) +{ + unsigned int maxSize = 0; + + switch ( field ) { + + case fieldCreationDate : + maxSize = 8; + break; + + case fieldTitle : + case fieldAuthor : + case fieldCopyright : + case fieldDescription : + maxSize = 0xFFFF; + break; + + case fieldCopyrightURL : +#if ! Exclude_LicenseURL_Recon + case fieldLicenseURL : +#endif + maxSize = 0xFFFFFFFF; + break; + + default: + break; + + } + + return maxSize; + +} + +// ================================================================================================= + +void ASF_LegacyManager::SetObjectExists ( objectType object ) +{ + objectsExisting |= object; +} + +// ================================================================================================= + +void ASF_LegacyManager::SetBroadcast ( const bool broadcast ) +{ + broadcastSet = broadcast; +} + +// ================================================================================================= + +bool ASF_LegacyManager::GetBroadcast() +{ + return broadcastSet; +} + +// ================================================================================================= + +void ASF_LegacyManager::ComputeDigest() +{ + MD5_CTX context; + MD5_Digest digest; + char buffer[40]; + + MD5Init ( &context ); + digestStr.clear(); + digestStr.reserve ( 160 ); + + for ( int type=0; type < fieldLast; ++type ) { + + if (fields[type].size ( ) > 0 ) { + snprintf ( buffer, sizeof(buffer), "%d,", type ); + digestStr.append ( buffer ); + MD5Update ( &context, (XMP_Uns8*)fields[type].data(), fields[type].size() ); + } + + } + + if( digestStr.size() > 0 ) digestStr[digestStr.size()-1] = ';'; + + MD5Final ( digest, &context ); + + size_t in, out; + for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) { + XMP_Uns8 byte = digest[in]; + buffer[out] = ReconcileUtils::kHexDigits [ byte >> 4 ]; + buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ]; + } + buffer[32] = 0; + + digestStr.append ( buffer ); + + digestComputed = true; + +} + +// ================================================================================================= + +bool ASF_LegacyManager::CheckDigest ( const SXMPMeta& xmp ) +{ + bool ret = false; + + if ( ! digestComputed ) this->ComputeDigest(); + + std::string oldDigest; + + if ( xmp.GetProperty ( kXMP_NS_ASF, "NativeDigest", &oldDigest, 0 ) ) { + ret = (digestStr == oldDigest); + } + + return ret; + +} + +// ================================================================================================= + +void ASF_LegacyManager::SetDigest ( SXMPMeta* xmp ) +{ + if ( ! digestComputed ) this->ComputeDigest(); + + xmp->SetProperty ( kXMP_NS_ASF, "NativeDigest", digestStr.c_str() ); + +} + +// ================================================================================================= + +void ASF_LegacyManager::ImportLegacy ( SXMPMeta* xmp ) +{ + std::string utf8; + + if ( ! broadcastSet ) { + ConvertMSDateToISODate ( fields[fieldCreationDate], &utf8 ); + if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", utf8.c_str(), kXMP_DeleteExisting ); + } + + FromUTF16 ( (UTF16Unit*)fields[fieldTitle].c_str(), (fields[fieldTitle].size() / 2), &utf8, false ); + if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); + + xmp->DeleteProperty ( kXMP_NS_DC, "creator" ); + FromUTF16 ( (UTF16Unit*)fields[fieldAuthor].c_str(), (fields[fieldAuthor].size() / 2), &utf8, false ); + if ( ! utf8.empty() ) SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator", + (kXMP_PropArrayIsOrdered | kXMPUtil_AllowCommas), utf8.c_str() ); + + FromUTF16 ( (UTF16Unit*)fields[fieldCopyright].c_str(), (fields[fieldCopyright].size() / 2), &utf8, false ); + if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); + + FromUTF16 ( (UTF16Unit*)fields[fieldDescription].c_str(), (fields[fieldDescription].size() / 2), &utf8, false ); + if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); + + if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting ); + +#if ! Exclude_LicenseURL_Recon + if ( ! fields[fieldLicenseURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting ); +#endif + + imported = true; + +} + +// ================================================================================================= + +int ASF_LegacyManager::ExportLegacy ( const SXMPMeta& xmp ) +{ + int changed = 0; + objectsToExport = 0; + legacyDiff = 0; + + std::string utf8; + std::string utf16; + XMP_OptionBits flags; + + if ( ! broadcastSet ) { + if ( xmp.GetProperty ( kXMP_NS_XMP, "CreateDate", &utf8, &flags ) ) { + std::string date; + ConvertISODateToMSDate ( utf8, &date ); + if ( fields[fieldCreationDate] != date ) { + legacyDiff += date.size(); + legacyDiff -= fields[fieldCreationDate].size(); + this->SetField ( fieldCreationDate, date ); + objectsToExport |= objectFileProperties; + changed ++; + } + } + } + + if ( xmp.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &utf8, &flags ) ) { + NormalizeStringTrailingNull ( utf8 ); + ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); + if ( fields[fieldTitle] != utf16 ) { + legacyDiff += utf16.size(); + legacyDiff -= fields[fieldTitle].size(); + this->SetField ( fieldTitle, utf16 ); + objectsToExport |= objectContentDescription; + changed ++; + } + } + + utf8.clear(); + SXMPUtils::CatenateArrayItems ( xmp, kXMP_NS_DC, "creator", 0, 0, kXMPUtil_AllowCommas, &utf8 ); + if ( ! utf8.empty() ) { + NormalizeStringTrailingNull ( utf8 ); + ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); + if ( fields[fieldAuthor] != utf16 ) { + legacyDiff += utf16.size(); + legacyDiff -= fields[fieldAuthor].size(); + this->SetField ( fieldAuthor, utf16 ); + objectsToExport |= objectContentDescription; + changed ++; + } + } + + if ( xmp.GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", 0, &utf8, &flags ) ) { + NormalizeStringTrailingNull ( utf8 ); + ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); + if ( fields[fieldCopyright] != utf16 ) { + legacyDiff += utf16.size(); + legacyDiff -= fields[fieldCopyright].size(); + this->SetField ( fieldCopyright, utf16 ); + objectsToExport |= objectContentDescription; + changed ++; + } + } + + if ( xmp.GetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", 0, &utf8, &flags ) ) { + NormalizeStringTrailingNull ( utf8 ); + ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); + if ( fields[fieldDescription] != utf16 ) { + legacyDiff += utf16.size(); + legacyDiff -= fields[fieldDescription].size(); + this->SetField ( fieldDescription, utf16 ); + objectsToExport |= objectContentDescription; + changed ++; + } + } + + if ( xmp.GetProperty ( kXMP_NS_XMP_Rights, "WebStatement", &utf8, &flags ) ) { + NormalizeStringTrailingNull ( utf8 ); + if ( fields[fieldCopyrightURL] != utf8 ) { + legacyDiff += utf8.size(); + legacyDiff -= fields[fieldCopyrightURL].size(); + this->SetField ( fieldCopyrightURL, utf8 ); + objectsToExport |= objectContentBranding; + changed ++; + } + } + +#if ! Exclude_LicenseURL_Recon + if ( xmp.GetProperty ( kXMP_NS_XMP_Rights, "Certificate", &utf8, &flags ) ) { + NormalizeStringTrailingNull ( utf8 ); + if ( fields[fieldLicenseURL] != utf8 ) { + legacyDiff += utf8.size(); + legacyDiff -= fields[fieldLicenseURL].size(); + this->SetField ( fieldLicenseURL, utf8 ); + objectsToExport |= objectContentEncryption; + changed ++; + } + } +#endif + + // find objects, that would need to be created on legacy export + int newObjects = (objectsToExport & !objectsExisting); + + // calculate minimum storage for new objects, that might be created on export + if ( newObjects & objectContentDescription ) + legacyDiff += sizeContentDescription; + if ( newObjects & objectContentBranding ) + legacyDiff += sizeContentBranding; + if ( newObjects & objectContentEncryption ) + legacyDiff += sizeContentEncryption; + + ComputeDigest(); + + return changed; + +} + +// ================================================================================================= + +bool ASF_LegacyManager::hasLegacyChanged() +{ + return (objectsToExport != 0); +} + +// ================================================================================================= + +XMP_Int64 ASF_LegacyManager::getLegacyDiff() +{ + return (this->hasLegacyChanged() ? legacyDiff : 0); +} + +// ================================================================================================= + +int ASF_LegacyManager::changedObjects() +{ + return objectsToExport; +} + +// ================================================================================================= + +void ASF_LegacyManager::SetPadding ( XMP_Int64 _padding ) +{ + padding = _padding; +} + +// ================================================================================================= + +XMP_Int64 ASF_LegacyManager::GetPadding() +{ + return padding; +} + +// ================================================================================================= + +std::string ASF_LegacyManager::NormalizeStringDisplayASCII ( std::string& operand ) +{ + std::basic_string<char>::iterator current = operand.begin(); + std::basic_string<char>::iterator end = operand.end();; + + for ( ; (current != end); ++current ) { + char element = *current; + if ( ( (element < 0x21) && (element != 0x00)) || (element > 0x7e) ) { + *current = '?'; + } + } + + return operand; + +} + +// ================================================================================================= + +std::string ASF_LegacyManager::NormalizeStringTrailingNull ( std::string& operand ) +{ + if ( ( operand.size() > 0) && (operand[operand.size() - 1] != '\0') ) { + operand.append ( 1, '\0' ); + } + + return operand; + +} + +// ================================================================================================= + +int ASF_LegacyManager::DaysInMonth ( XMP_Int32 year, XMP_Int32 month ) +{ + + static short daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + + int days = daysInMonth [ month ]; + if ( (month == 2) && IsLeapYear ( year ) ) days += 1; + + return days; + +} + +// ================================================================================================= + +bool ASF_LegacyManager::IsLeapYear ( long year ) +{ + + if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0. + + if ( (year % 4) != 0 ) return false; // Not a multiple of 4. + if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100. + if ( (year % 400) == 0 ) return true; // A multiple of 400. + + return false; // A multiple of 100 but not a multiple of 400. + +} + +// ================================================================================================= + +void ASF_LegacyManager::ConvertMSDateToISODate ( std::string& source, std::string* dest ) +{ + + XMP_Int64 creationDate = GetUns64LE ( source.c_str() ); + XMP_Int64 totalSecs = creationDate / (10*1000*1000); + XMP_Int32 nanoSec = ( ( XMP_Int32) (creationDate - (totalSecs * 10*1000*1000)) ) * 100; + + XMP_Int32 days = (XMP_Int32) (totalSecs / 86400); + totalSecs -= ( ( XMP_Int64)days * 86400 ); + + XMP_Int32 hour = (XMP_Int32) (totalSecs / 3600); + totalSecs -= ( ( XMP_Int64)hour * 3600 ); + + XMP_Int32 minute = (XMP_Int32) (totalSecs / 60); + totalSecs -= ( ( XMP_Int64)minute * 60 ); + + XMP_Int32 second = (XMP_Int32)totalSecs; + + // A little more simple code converts this to an XMP date string: + + XMP_DateTime date; + memset ( &date, 0, sizeof ( date ) ); + + date.year = 1601; // The MS date origin. + date.month = 1; + date.day = 1; + + date.day += days; // Add in the delta. + date.hour = hour; + date.minute = minute; + date.second = second; + date.nanoSecond = nanoSec; + + date.hasTimeZone = true; // ! Needed for ConvertToUTCTime to do anything. + SXMPUtils::ConvertToUTCTime ( &date ); // Normalize the date/time. + SXMPUtils::ConvertFromDate ( date, dest ); // Convert to an ISO 8601 string. + +} + +// ================================================================================================= + +void ASF_LegacyManager::ConvertISODateToMSDate ( std::string& source, std::string* dest ) +{ + XMP_DateTime date; + SXMPUtils::ConvertToDate ( source, &date ); + SXMPUtils::ConvertToUTCTime ( &date ); + + XMP_Int64 creationDate; + creationDate = date.nanoSecond / 100; + creationDate += (XMP_Int64 ( date.second) * (10*1000*1000) ); + creationDate += (XMP_Int64 ( date.minute) * 60 * (10*1000*1000) ); + creationDate += (XMP_Int64 ( date.hour) * 3600 * (10*1000*1000) ); + + XMP_Int32 days = (date.day - 1); + + --date.month; + while ( date.month >= 1 ) { + days += DaysInMonth ( date.year, date.month ); + --date.month; + } + + --date.year; + while ( date.year >= 1601 ) { + days += (IsLeapYear ( date.year) ? 366 : 365 ); + --date.year; + } + + creationDate += (XMP_Int64 ( days) * 86400 * (10*1000*1000) ); + + creationDate = GetUns64LE ( &creationDate ); + dest->assign ( (const char*)&creationDate, 8 ); + +} |