/paplayer/mac-3.99-u4-b5/src/MACLib/APEHeader.cpp
C++ | 336 lines | 241 code | 61 blank | 34 comment | 69 complexity | 77ae45f12adb828c6a2bca14637980a4 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.1, CC-BY-SA-3.0, BSD-3-Clause, AGPL-1.0, LGPL-3.0
- #include "All.h"
- #include "APEHeader.h"
- #include "MACLib.h"
- #include "APEInfo.h"
- // TODO: should push and pop the file position
- CAPEHeader::CAPEHeader(CIO * pIO)
- {
- m_pIO = pIO;
- }
- CAPEHeader::~CAPEHeader()
- {
- }
- int CAPEHeader::FindDescriptor(BOOL bSeek)
- {
- // store the original location and seek to the beginning
- int nOriginalFileLocation = m_pIO->GetPosition();
- m_pIO->Seek(0, FILE_BEGIN);
- // set the default junk bytes to 0
- int nJunkBytes = 0;
- // skip an ID3v2 tag (which we really don't support anyway...)
- unsigned int nBytesRead = 0;
- unsigned char cID3v2Header[10];
- m_pIO->Read((unsigned char *) cID3v2Header, 10, &nBytesRead);
- if (cID3v2Header[0] == 'I' && cID3v2Header[1] == 'D' && cID3v2Header[2] == '3')
- {
- // why is it so hard to figure the lenght of an ID3v2 tag ?!?
- // unsigned int nLength = *((unsigned int *) &cID3v2Header[6]);
- unsigned int nSyncSafeLength = 0;
- nSyncSafeLength = (cID3v2Header[6] & 127) << 21;
- nSyncSafeLength += (cID3v2Header[7] & 127) << 14;
- nSyncSafeLength += (cID3v2Header[8] & 127) << 7;
- nSyncSafeLength += (cID3v2Header[9] & 127);
- BOOL bHasTagFooter = FALSE;
- if (cID3v2Header[5] & 16)
- {
- bHasTagFooter = TRUE;
- nJunkBytes = nSyncSafeLength + 20;
- }
- else
- {
- nJunkBytes = nSyncSafeLength + 10;
- }
- // error check
- if (cID3v2Header[5] & 64)
- {
- // this ID3v2 length calculator algorithm can't cope with extended headers
- // we should be ok though, because the scan for the MAC header below should
- // really do the trick
- }
- m_pIO->Seek(nJunkBytes, FILE_BEGIN);
- // scan for padding (slow and stupid, but who cares here...)
- if (!bHasTagFooter)
- {
- char cTemp = 0;
- m_pIO->Read((unsigned char *) &cTemp, 1, &nBytesRead);
- while (cTemp == 0 && nBytesRead == 1)
- {
- nJunkBytes++;
- m_pIO->Read((unsigned char *) &cTemp, 1, &nBytesRead);
- }
- }
- }
- m_pIO->Seek(nJunkBytes, FILE_BEGIN);
- // scan until we hit the APE_DESCRIPTOR, the end of the file, or 1 MB later
- unsigned int nGoalID = (' ' << 24) | ('C' << 16) | ('A' << 8) | ('M');
- unsigned int nReadID = 0;
- int nRetVal = m_pIO->Read(&nReadID, 4, &nBytesRead);
- if (nRetVal != 0 || nBytesRead != 4) return ERROR_UNDEFINED;
- nBytesRead = 1;
- int nScanBytes = 0;
- while ((nGoalID != swap_int32(nReadID)) && (nBytesRead == 1) && (nScanBytes < (1024 * 1024)))
- {
- unsigned char cTemp;
- m_pIO->Read(&cTemp, 1, &nBytesRead);
- nReadID = (((unsigned int) cTemp) << 24) | (nReadID >> 8);
- nJunkBytes++;
- nScanBytes++;
- }
- nReadID = swap_int32(nReadID);
- if (nGoalID != nReadID)
- nJunkBytes = -1;
- // seek to the proper place (depending on result and settings)
- if (bSeek && (nJunkBytes != -1))
- {
- // successfully found the start of the file (seek to it and return)
- m_pIO->Seek(nJunkBytes, FILE_BEGIN);
- }
- else
- {
- // restore the original file pointer
- m_pIO->Seek(nOriginalFileLocation, FILE_BEGIN);
- }
- return nJunkBytes;
- }
- int CAPEHeader::Analyze(APE_FILE_INFO * pInfo)
- {
- // error check
- if ((m_pIO == NULL) || (pInfo == NULL))
- return ERROR_INVALID_FUNCTION_PARAMETER;
- // variables
- unsigned int nBytesRead = 0;
- // find the descriptor
- pInfo->nJunkHeaderBytes = FindDescriptor(TRUE);
- if (pInfo->nJunkHeaderBytes < 0)
- return ERROR_UNDEFINED;
- // read the first 8 bytes of the descriptor (ID and version)
- APE_COMMON_HEADER CommonHeader; memset(&CommonHeader, 0, sizeof(APE_COMMON_HEADER));
- m_pIO->Read(&CommonHeader, sizeof(APE_COMMON_HEADER), &nBytesRead);
- CommonHeader.nVersion = swap_int16(CommonHeader.nVersion);
- // make sure we're at the ID
- if (CommonHeader.cID[0] != 'M' || CommonHeader.cID[1] != 'A' || CommonHeader.cID[2] != 'C' || CommonHeader.cID[3] != ' ')
- return ERROR_UNDEFINED;
- int nRetVal = ERROR_UNDEFINED;
- if (CommonHeader.nVersion >= 3980)
- {
- // current header format
- nRetVal = AnalyzeCurrent(pInfo);
- }
- else
- {
- // legacy support
- nRetVal = AnalyzeOld(pInfo);
- }
- return nRetVal;
- }
- #ifdef WORDS_BIGENDIAN
- void swap_ape_header_old(APE_HEADER_OLD *header)
- {
- header->nVersion = swap_int16(header->nVersion);
- header->nCompressionLevel = swap_int16(header->nCompressionLevel);
- header->nFormatFlags = swap_int16(header->nFormatFlags);
- header->nChannels = swap_int16(header->nChannels);
- header->nSampleRate = swap_int32(header->nSampleRate);
- header->nHeaderBytes = swap_int32(header->nHeaderBytes);
- header->nTerminatingBytes = swap_int32(header->nTerminatingBytes);
- header->nTotalFrames = swap_int32(header->nTotalFrames);
- header->nFinalFrameBlocks = swap_int32(header->nFinalFrameBlocks);
- }
- #else
- #define swap_ape_header_old(a) {}
- #endif
- int CAPEHeader::AnalyzeCurrent(APE_FILE_INFO * pInfo)
- {
- // variable declares
- unsigned int nBytesRead = 0;
- pInfo->spAPEDescriptor.Assign(new APE_DESCRIPTOR); memset(pInfo->spAPEDescriptor, 0, sizeof(APE_DESCRIPTOR));
- APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
- // read the descriptor
- m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
- m_pIO->Read(pInfo->spAPEDescriptor, sizeof(APE_DESCRIPTOR), &nBytesRead);
- swap_ape_descriptor((APE_DESCRIPTOR *)pInfo->spAPEDescriptor);
- if ((pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead) > 0)
- m_pIO->Seek(pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead, FILE_CURRENT);
- // read the header
- m_pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
- swap_ape_header(&APEHeader);
- if ((pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead) > 0)
- m_pIO->Seek(pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead, FILE_CURRENT);
- // fill the APE info structure
- pInfo->nVersion = int(pInfo->spAPEDescriptor->nVersion);
- pInfo->nCompressionLevel = int(APEHeader.nCompressionLevel);
- pInfo->nFormatFlags = int(APEHeader.nFormatFlags);
- pInfo->nTotalFrames = int(APEHeader.nTotalFrames);
- pInfo->nFinalFrameBlocks = int(APEHeader.nFinalFrameBlocks);
- pInfo->nBlocksPerFrame = int(APEHeader.nBlocksPerFrame);
- pInfo->nChannels = int(APEHeader.nChannels);
- pInfo->nSampleRate = int(APEHeader.nSampleRate);
- pInfo->nBitsPerSample = int(APEHeader.nBitsPerSample);
- pInfo->nBytesPerSample = pInfo->nBitsPerSample / 8;
- pInfo->nBlockAlign = pInfo->nBytesPerSample * pInfo->nChannels;
- pInfo->nTotalBlocks = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames - 1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
- pInfo->nWAVHeaderBytes = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : pInfo->spAPEDescriptor->nHeaderDataBytes;
- pInfo->nWAVTerminatingBytes = pInfo->spAPEDescriptor->nTerminatingDataBytes;
- pInfo->nWAVDataBytes = pInfo->nTotalBlocks * pInfo->nBlockAlign;
- pInfo->nWAVTotalBytes = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
- pInfo->nAPETotalBytes = m_pIO->GetSize();
- pInfo->nLengthMS = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
- pInfo->nAverageBitrate = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
- pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
- pInfo->nSeekTableElements = pInfo->spAPEDescriptor->nSeekTableBytes / 4;
- // get the seek tables (really no reason to get the whole thing if there's extra)
- pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
- if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
- m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
- #ifdef WORDS_BIGENDIAN
- uint32 *ptr = pInfo->spSeekByteTable.GetPtr();
- int i = 0;
- for (i = 0; i < pInfo->nSeekTableElements; i ++)
- {
- ptr[i] = swap_int32(ptr[i]);
- }
- #endif
- // get the wave header
- if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
- {
- pInfo->spWaveHeaderData.Assign(new unsigned char [pInfo->nWAVHeaderBytes], TRUE);
- if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
- m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, pInfo->nWAVHeaderBytes, &nBytesRead);
- }
- return ERROR_SUCCESS;
- }
- int CAPEHeader::AnalyzeOld(APE_FILE_INFO * pInfo)
- {
- // variable declares
- unsigned int nBytesRead = 0;
- // read the MAC header from the file
- APE_HEADER_OLD APEHeader;
- m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
- m_pIO->Read((unsigned char *) &APEHeader, sizeof(APEHeader), &nBytesRead);
- swap_ape_header_old(&APEHeader);
- // fail on 0 length APE files (catches non-finalized APE files)
- if (APEHeader.nTotalFrames == 0)
- return ERROR_UNDEFINED;
- int nPeakLevel = -1;
- if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL)
- {
- m_pIO->Read((unsigned char *) &nPeakLevel, 4, &nBytesRead);
- nPeakLevel = swap_int32(nPeakLevel);
- }
- if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS)
- {
- m_pIO->Read((unsigned char *) &pInfo->nSeekTableElements, 4, &nBytesRead);
- pInfo->nSeekTableElements = swap_int32(pInfo->nSeekTableElements);
- }
- else
- pInfo->nSeekTableElements = APEHeader.nTotalFrames;
-
- // fill the APE info structure
- pInfo->nVersion = int(APEHeader.nVersion);
- pInfo->nCompressionLevel = int(APEHeader.nCompressionLevel);
- pInfo->nFormatFlags = int(APEHeader.nFormatFlags);
- pInfo->nTotalFrames = int(APEHeader.nTotalFrames);
- pInfo->nFinalFrameBlocks = int(APEHeader.nFinalFrameBlocks);
- pInfo->nBlocksPerFrame = ((APEHeader.nVersion >= 3900) || ((APEHeader.nVersion >= 3800) && (APEHeader.nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH))) ? 73728 : 9216;
- if ((APEHeader.nVersion >= 3950)) pInfo->nBlocksPerFrame = 73728 * 4;
- pInfo->nChannels = int(APEHeader.nChannels);
- pInfo->nSampleRate = int(APEHeader.nSampleRate);
- pInfo->nBitsPerSample = (pInfo->nFormatFlags & MAC_FORMAT_FLAG_8_BIT) ? 8 : ((pInfo->nFormatFlags & MAC_FORMAT_FLAG_24_BIT) ? 24 : 16);
- pInfo->nBytesPerSample = pInfo->nBitsPerSample / 8;
- pInfo->nBlockAlign = pInfo->nBytesPerSample * pInfo->nChannels;
- pInfo->nTotalBlocks = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames - 1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
- pInfo->nWAVHeaderBytes = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : APEHeader.nHeaderBytes;
- pInfo->nWAVTerminatingBytes = int(APEHeader.nTerminatingBytes);
- pInfo->nWAVDataBytes = pInfo->nTotalBlocks * pInfo->nBlockAlign;
- pInfo->nWAVTotalBytes = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
- pInfo->nAPETotalBytes = m_pIO->GetSize();
- pInfo->nLengthMS = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
- pInfo->nAverageBitrate = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
- pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
- // get the wave header
- if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
- {
- pInfo->spWaveHeaderData.Assign(new unsigned char [APEHeader.nHeaderBytes], TRUE);
- if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
- m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, APEHeader.nHeaderBytes, &nBytesRead);
- }
- // get the seek tables (really no reason to get the whole thing if there's extra)
- pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
- if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
- m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
- #ifdef WORDS_BIGENDIAN
- uint32 *ptr = pInfo->spSeekByteTable.GetPtr();
- int i = 0;
- for (i = 0; i < pInfo->nSeekTableElements; i ++)
- {
- ptr[i] = swap_int32(ptr[i]);
- }
- #endif
- if (APEHeader.nVersion <= 3800)
- {
- pInfo->spSeekBitTable.Assign(new unsigned char [pInfo->nSeekTableElements], TRUE);
- if (pInfo->spSeekBitTable == NULL) { return ERROR_UNDEFINED; }
- m_pIO->Read((unsigned char *) pInfo->spSeekBitTable, pInfo->nSeekTableElements, &nBytesRead);
- }
- return ERROR_SUCCESS;
- }