📄 id3_support.cpp
字号:
if ( (bEncoding == 0) || (bEncoding == 3) ) { dwOffset += (strlen ( &strData[3] ) + 1); // Skip the descriptor and nul. } else { UTF16Unit* u16Ptr = (UTF16Unit*) (&strData[3]); for ( ; *u16Ptr != 0; ++u16Ptr ) dwOffset += 2; // Skip the descriptor. dwOffset += 2; // Skip the nul also. } if ( dwOffset >= dwBufferSize ) return false; dwBufferSize -= dwOffset; sdPos = dwOffset; #if Trace_ID3_Support fprintf ( stderr, " COMM frame, dwOffset %d\n", dwOffset ); #endif } // Encoding translation switch ( bEncoding ) { case 1: // UTF-16 with a BOM. (Might be missing for empty string.) case 2: // Big endian UTF-16 with no BOM. { bool bigEndian = true; // Assume big endian if no BOM. UTF16Unit* u16Ptr = (UTF16Unit*) &strData[sdPos]; if ( GetUns16BE ( u16Ptr ) == 0xFEFF ) { ++u16Ptr; // Don't translate the BOM. } else if ( GetUns16BE ( u16Ptr ) == 0xFFFE ) { bigEndian = false; ++u16Ptr; // Don't translate the BOM. } size_t u16Len = 0; // Count the UTF-16 units, not bytes. for ( UTF16Unit* temp = u16Ptr; *temp != 0; ++temp ) ++u16Len; std::string utf8Str; FromUTF16 ( u16Ptr, u16Len, &utf8Str, bigEndian ); if ( utf8Str.size() >= (sizeof(strData) - sdPos) ) return false; strcpy ( &strData[sdPos], utf8Str.c_str() ); // AUDIT: Protected by the above check. } break; case 0: // ISO Latin-1 (8859-1). { std::string utf8Str; char* localPtr = &strData[sdPos]; size_t localLen = dwBufferSize; ReconcileUtils::Latin1ToUTF8 ( localPtr, localLen, &utf8Str ); if ( utf8Str.size() >= (sizeof(strData) - sdPos) ) return false; strcpy ( &strData[sdPos], utf8Str.c_str() ); // AUDIT: Protected by the above check. } break; case 3: // UTF-8 default: // Handled appropriately break; } char * strTemp = &strData[sdPos]; if ( strcmp ( strFrame, "TCON" ) == 0 ) { char str[TAG_MAX_SIZE]; str[0] = 0; if ( strlen ( &strData[sdPos] ) >= sizeof(str) ) return false; strcpy ( str, &strData[sdPos] ); // AUDIT: Protected by the above check. #if Trace_ID3_Support fprintf ( stderr, " TCON frame, first char '%c'\n", str[0] ); #endif // Genre: let's get the "string" value if ( str[0] == '(' ) { int iGenre = atoi(str+1); if ( (iGenre > 0) && (iGenre < 127) ) { strTemp = Genres[iGenre]; } else { strTemp = Genres[12]; } } else { // Text, let's "try" to find it anyway int i = 0; for ( i=0; i < 127; ++i ) { if ( stricmp ( str, Genres[i] ) == 0 ) { strTemp = Genres[i]; // Found, let's use the one in the list break; } } if ( i == 127 ) strTemp = Genres[12]; // Not found } } #if Trace_ID3_Support fprintf ( stderr, " Have data, length %d, \"%s\"\n", strlen(strTemp), strTemp ); #endif if ( strlen(strTemp) >= dwSizeIn ) return false; strcpy ( buffer, strTemp ); // AUDIT: Protected by the above check. } return true;}// =================================================================================================bool AddXMPTagToID3Buffer ( char * strCur, unsigned long * pdwCurOffset, unsigned long dwMaxSize, XMP_Uns8 bVersion, char *strFrameName, const char * strXMPTagTemp, unsigned long dwXMPLengthTemp ){ char strGenre[64]; const char * strXMPTag = strXMPTagTemp; XMP_Int32 dwCurOffset = *pdwCurOffset; XMP_Uns8 bEncoding = 0; long dwXMPLength = dwXMPLengthTemp; if ( dwXMPLength == 0 ) return false; if ( strcmp ( strFrameName, "TCON" ) == 0 ) { // Genre: we need to get the number back... int iFound = 12; for ( int i=0; i < 127; ++i ) { if ( stricmp ( strXMPTag, Genres[i] ) == 0 ) { iFound = i; // Found break; } } snprintf ( strGenre, sizeof(strGenre), "(%d)", iFound ); // AUDIT: Using sizeof(strGenre) is safe. strXMPTag = strGenre; dwXMPLength = strlen(strXMPTag); } // Stick with the ID3v2.3 encoding choices, they are a proper subset of ID3v2.4. // 0 - ISO Latin-1 // 1 - UTF-16 with BOM // For 3rd party reliability we always write UTF-16 as little endian. For example, Windows // Media Player fails to honor the BOM, it assumes little endian. std::string tempLatin1, tempUTF8; ReconcileUtils::UTF8ToLatin1 ( strXMPTag, dwXMPLength, &tempLatin1 ); ReconcileUtils::Latin1ToUTF8 ( tempLatin1.data(), tempLatin1.size(), &tempUTF8 ); if ( ((size_t)dwXMPLength != tempUTF8.size()) || (memcmp ( strXMPTag, tempUTF8.data(), dwXMPLength ) != 0) ) { bEncoding = 1; // Will convert to UTF-16 later. } else { strXMPTag = tempLatin1.c_str(); // Use the Latin-1 encoding for output. dwXMPLength = tempLatin1.size(); } std::string strUTF16; if ( bEncoding == 1 ) { ToUTF16 ( (UTF8Unit*)strXMPTag, dwXMPLength, &strUTF16, false /* little endian */ ); dwXMPLength = strUTF16.size() + 2; // ! Include the (to be inserted) BOM in the count. } // Frame Structure // Frame ID $xx xx xx xx (four characters) // Size 4 * %0xxxxxxx <<--- IMPORTANT NOTE: This is true only in v4.0 (v3.0 uses a UInt32) // Flags $xx xx // Encoding $xx (Not included in the frame header) // Special case: "COMM" which we have to include "XXX\0" in front of it (also not included in the frame header) unsigned long dwFrameSize = dwXMPLength + 1; // 1 == Encoding; bool fCOMM = (strcmp ( strFrameName, "COMM" ) == 0); if ( fCOMM ) { dwFrameSize += 3; // The "XXX" language part. dwFrameSize += ((bEncoding == 0) ? 1 : 4 ); // The empty descriptor string. } if ( (dwCurOffset + k_dwFrameHeaderSize + dwFrameSize) > dwMaxSize ) return false; unsigned long dwCalculated = CalculateSize ( bVersion, dwFrameSize ); // FrameID if ( (dwMaxSize - dwCurOffset) < 4 ) return false; memcpy ( strCur+dwCurOffset, strFrameName, 4 ); // AUDIT: Protected by the above check. dwCurOffset += 4; // Frame Size - written as big endian strCur[dwCurOffset] = (char)(dwCalculated >> 24); ++dwCurOffset; strCur[dwCurOffset] = (char)((dwCalculated >> 16) & 0xFF); ++dwCurOffset; strCur[dwCurOffset] = (char)((dwCalculated >> 8) & 0xFF); ++dwCurOffset; strCur[dwCurOffset] = (char)(dwCalculated & 0xFF); ++dwCurOffset; // Flags strCur[dwCurOffset] = 0; ++dwCurOffset; strCur[dwCurOffset] = 0; ++dwCurOffset; // Encoding strCur[dwCurOffset] = bEncoding; ++dwCurOffset; // COMM extras: XXX language and empty encoded descriptor string. if ( fCOMM ) { if ( (dwMaxSize - dwCurOffset) < 3 ) return false; memcpy ( strCur+dwCurOffset, "XXX", 3 ); // AUDIT: Protected by the above check. dwCurOffset += 3; if ( bEncoding == 0 ) { strCur[dwCurOffset] = 0; ++dwCurOffset; } else { strCur[dwCurOffset] = 0xFF; ++dwCurOffset; strCur[dwCurOffset] = 0xFE; ++dwCurOffset; strCur[dwCurOffset] = 0; ++dwCurOffset; strCur[dwCurOffset] = 0; ++dwCurOffset; } } if ( bEncoding == 1 ) { // Add the BOM "FFFE" strCur[dwCurOffset] = 0xFF; ++dwCurOffset; strCur[dwCurOffset] = 0xFE; ++dwCurOffset; dwXMPLength -= 2; // The BOM was included above. // Copy the Unicode data if ( (long)(dwMaxSize - dwCurOffset) < dwXMPLength ) return false; memcpy ( strCur+dwCurOffset, strUTF16.data(), dwXMPLength ); // AUDIT: Protected by the above check. dwCurOffset += dwXMPLength; } else { // Copy the data if ( (long)(dwMaxSize - dwCurOffset) < dwXMPLength ) return false; memcpy ( strCur+dwCurOffset, strXMPTag, dwXMPLength ); // AUDIT: Protected by the above check. dwCurOffset += dwXMPLength; } *pdwCurOffset = dwCurOffset; return true;}// =================================================================================================static void OffsetAudioData ( LFA_FileRef inFileRef, XMP_Int64 audioOffset, XMP_Int64 oldAudioBase ){ enum { kBuffSize = 64*1024 }; XMP_Uns8 buffer [kBuffSize]; const XMP_Int64 posEOF = LFA_Measure ( inFileRef ); XMP_Int64 posCurrentCopy; // ! Must be a signed type! posCurrentCopy = posEOF; while ( posCurrentCopy >= (oldAudioBase + kBuffSize) ) { posCurrentCopy -= kBuffSize; // *** Xcode 2.3 seemed to generate bad code using a for loop. LFA_Seek ( inFileRef, posCurrentCopy, SEEK_SET ); LFA_Read ( inFileRef, buffer, kBuffSize ); LFA_Seek ( inFileRef, (posCurrentCopy + audioOffset), SEEK_SET ); LFA_Write ( inFileRef, buffer, kBuffSize ); } if ( posCurrentCopy != oldAudioBase ) { XMP_Uns32 remainder = (XMP_Uns32) (posCurrentCopy - oldAudioBase); XMP_Assert ( remainder < kBuffSize ); LFA_Seek ( inFileRef, oldAudioBase, SEEK_SET ); LFA_Read ( inFileRef, buffer, remainder ); LFA_Seek ( inFileRef, (oldAudioBase + audioOffset), SEEK_SET ); LFA_Write ( inFileRef, buffer, remainder ); }}// =================================================================================================bool SetMetaData ( LFA_FileRef inFileRef, char* strXMPPacket, unsigned long dwXMPPacketSize, char* strLegacyFrames, unsigned long dwFullLegacySize, bool fRecon ){ // The ID3 section layout: // ID3 header, 10 bytes // Unrecognized ID3 frames // Legacy ID3 metadata frames (artist, album, genre, etc.) // XMP frame, content is "XMP\0" plus the packet // padding // ID3 Buffer vars const unsigned long kiMaxBuffer = 100*1000; char szID3Buffer [kiMaxBuffer]; // Must be enough for the ID3 header, unknown ID3 frames, and legacy ID3 metadata. unsigned long id3BufferLen = 0; // The amount of stuff currently in the buffer. unsigned long dwOldID3ContentSize = 0; // The size of the existing ID3 content (not counting the header). unsigned long dwNewID3ContentSize = 0; // The size of the updated ID3 content (not counting the header). unsigned long newPadSize = 0; XMP_Uns8 bMajorVersion = 3; bool fFoundID3 = FindID3Tag ( inFileRef, dwOldID3ContentSize, bMajorVersion ); if ( (bMajorVersion > 4) || (bMajorVersion < 3) ) return false; // Not supported // Now that we know the version of the ID3 tag, let's format the size of the XMP frame. #define k_XMPPrefixSize (k_dwFrameHeaderSize + k_dwXMPLabelSize) char szXMPPrefix [k_XMPPrefixSize] = { 'P', 'R', 'I', 'V', 0, 0, 0, 0, 0, 0, 'X', 'M', 'P', 0 }; unsigned long dwXMPContentSize = k_dwXMPLabelSize + dwXMPPacketSize; unsigned long dwFullXMPFrameSize = k_dwFrameHeaderSize + dwXMPContentSize; unsigned long dwFormattedTemp = CalculateSize ( bMajorVersion, dwXMPContentSize ); szXMPPrefix[4] = (char)(dwFormattedTemp >> 24); szXMPPrefix[5] = (char)((dwFormattedTemp >> 16) & 0xFF); szXMPPrefix[6] = (char)((dwFormattedTemp >> 8) & 0xFF); szXMPPrefix[7] = (char)(dwFormattedTemp & 0xFF); // Set up the ID3 buffer with the ID3 header and any existing unrecognized ID3 frames. if ( ! fFoundID3 ) { // Case 1 - No id3v2 tag: Create the tag with the XMP frame. // Create the tag // ID3v2/file identifier "ID3" // ID3v2 version $03 00 // ID3v2 flags %abcd0000 // ID3v2 size 4 * %0xxxxxxx XMP_Assert ( dwOldID3ContentSize == 0 ); char szID3Header [k_dwTagHeaderSize] = { 'I', 'D', '3', 3, 0, 0, 0, 0, 0, 0 }; // Copy the ID3 header if ( sizeof(szID3Buffer) < k_dwTagHeaderSize ) return false; memcpy ( szID3Buffer, szID3Header, k_dwTagHeaderSize ); // AUDIT: Protected by the above check. id3BufferLen = k_dwTagHeaderSize; newPadSize = 100; dwNewID3ContentSize = dwFullLegacySize + dwFullXMPFrameSize + newPadSize; } else { // Case 2 - id3v2 tag is present // 1. Copy all the unknown tags // 2. Make the rest padding (to be used right there). if ( (k_dwFrameHeaderSize + dwOldID3ContentSize) > kiMaxBuffer ) { // The ID3Buffer is not big enough to fit the id3v2 tag... let's bail... return false; } LoadTagHeaderAndUnknownFrames ( inFileRef, szID3Buffer, fRecon, id3BufferLen ); unsigned long spareLen = (k_dwFrameHeaderSize + dwOldID3ContentSize) - id3BufferLen; if ( spareLen >= (dwFullLegacySize + dwFullXMPFrameSize) ) { // The exising ID3 header can hold the update. dwNewID3ContentSize = dwOldID3ContentSize; newPadSize = spareLen - (dwFullLegacySize + dwFullXMPFrameSize); } else { // The existing ID3 header is too small, it will have to grow. newPadSize = 100; dwNewID3ContentSize = (id3BufferLen - k_dwTagHeaderSize) + dwFullLegacySize + dwFullXMPFrameSize + newPadSize; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -