cog/Libraries/MAC/Files/Source/MACLib/APEHeader.cpp

324 lines
14 KiB
C++
Executable File

#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 nGoalID = ('M' << 24) | ('A' << 16) | ('C' << 8) | (' ');
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 != nReadID) && (nBytesRead == 1) && (nScanBytes < (1024 * 1024)))
{
unsigned char cTemp;
m_pIO->Read(&cTemp, 1, &nBytesRead);
nReadID = (((unsigned int) cTemp) << 24) | (nReadID >> 8);
nJunkBytes++;
nScanBytes++;
}
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_BAD_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_endian16(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;
}
int CAPEHeader::AnalyzeCurrent(APE_FILE_INFO * pInfo)
{
int i;
// 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);
pInfo->spAPEDescriptor->nVersion = swap_endian32(pInfo->spAPEDescriptor->nVersion);
pInfo->spAPEDescriptor->nDescriptorBytes = swap_endian32(pInfo->spAPEDescriptor->nDescriptorBytes);
pInfo->spAPEDescriptor->nHeaderBytes = swap_endian32(pInfo->spAPEDescriptor->nHeaderBytes);
pInfo->spAPEDescriptor->nSeekTableBytes = swap_endian32(pInfo->spAPEDescriptor->nSeekTableBytes);
pInfo->spAPEDescriptor->nHeaderDataBytes = swap_endian32(pInfo->spAPEDescriptor->nHeaderDataBytes);
pInfo->spAPEDescriptor->nAPEFrameDataBytes = swap_endian32(pInfo->spAPEDescriptor->nAPEFrameDataBytes);
pInfo->spAPEDescriptor->nAPEFrameDataBytesHigh = swap_endian32(pInfo->spAPEDescriptor->nAPEFrameDataBytesHigh);
pInfo->spAPEDescriptor->nTerminatingDataBytes = swap_endian32(pInfo->spAPEDescriptor->nTerminatingDataBytes);
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);
APEHeader.nCompressionLevel = swap_endian16(APEHeader.nCompressionLevel);
APEHeader.nFormatFlags = swap_endian16(APEHeader.nFormatFlags);
APEHeader.nBlocksPerFrame = swap_endian32(APEHeader.nBlocksPerFrame);
APEHeader.nFinalFrameBlocks = swap_endian32(APEHeader.nFinalFrameBlocks);
APEHeader.nTotalFrames = swap_endian32(APEHeader.nTotalFrames);
APEHeader.nBitsPerSample = swap_endian16(APEHeader.nBitsPerSample);
APEHeader.nChannels = swap_endian16(APEHeader.nChannels);
APEHeader.nSampleRate = swap_endian32(APEHeader.nSampleRate);
//printf("%d,%d,%d,%d,%d,%d,%d,%d\n",APEHeader.nCompressionLevel,APEHeader.nFormatFlags,APEHeader.nBlocksPerFrame,APEHeader.nFinalFrameBlocks,APEHeader.nTotalFrames,APEHeader.nBitsPerSample,APEHeader.nChannels,APEHeader.nSampleRate);
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; }
unsigned int *ptr = pInfo->spSeekByteTable.GetPtr();
//m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
m_pIO->Read((unsigned char *) ptr, 4 * pInfo->nSeekTableElements, &nBytesRead);
for(i=0;i<(pInfo->nSeekTableElements);i++) {
ptr[i] = swap_endian32(ptr[i]);
}
// 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;
int i;
// 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);
APEHeader.nVersion = swap_endian16(APEHeader.nVersion);
APEHeader.nCompressionLevel = swap_endian16(APEHeader.nCompressionLevel);
APEHeader.nFormatFlags = swap_endian16(APEHeader.nFormatFlags);
APEHeader.nChannels = swap_endian16(APEHeader.nChannels);
APEHeader.nSampleRate = swap_endian32(APEHeader.nSampleRate);
APEHeader.nHeaderBytes = swap_endian32(APEHeader.nHeaderBytes);
APEHeader.nTerminatingBytes = swap_endian32(APEHeader.nTerminatingBytes);
APEHeader.nTotalFrames = swap_endian32(APEHeader.nTotalFrames);
APEHeader.nFinalFrameBlocks = swap_endian32(APEHeader.nFinalFrameBlocks);
// 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_endian32(nPeakLevel);
}
if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) {
m_pIO->Read((unsigned char *) &pInfo->nSeekTableElements, 4, &nBytesRead);
pInfo->nSeekTableElements = swap_endian32(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; }
unsigned int *ptr = pInfo->spSeekByteTable.GetPtr();
//m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
m_pIO->Read((unsigned char *) ptr, 4 * pInfo->nSeekTableElements, &nBytesRead);
for(i=0;i<(pInfo->nSeekTableElements);i++) {
ptr[i] = swap_endian32(ptr[i]);
}
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;
}