1#include "All.h"
2#include "APEHeader.h"
3#include "MACLib.h"
4#include "APEInfo.h"
5
6// TODO: should push and pop the file position
7
8CAPEHeader::CAPEHeader(CIO * pIO)
9{
10    m_pIO = pIO;
11}
12
13CAPEHeader::~CAPEHeader()
14{
15
16}
17
18int CAPEHeader::FindDescriptor(BOOL bSeek)
19{
20    // store the original location and seek to the beginning
21    int nOriginalFileLocation = m_pIO->GetPosition();
22    m_pIO->Seek(0, FILE_BEGIN);
23
24    // set the default junk bytes to 0
25    int nJunkBytes = 0;
26
27    // skip an ID3v2 tag (which we really don't support anyway...)
28    unsigned int nBytesRead = 0;
29    unsigned char cID3v2Header[10];
30    m_pIO->Read((unsigned char *) cID3v2Header, 10, &nBytesRead);
31    if (cID3v2Header[0] == 'I' && cID3v2Header[1] == 'D' && cID3v2Header[2] == '3')
32    {
33        // why is it so hard to figure the lenght of an ID3v2 tag ?!?
34//        unsigned int nLength = *((unsigned int *) &cID3v2Header[6]);
35
36        unsigned int nSyncSafeLength = 0;
37        nSyncSafeLength = (cID3v2Header[6] & 127) << 21;
38        nSyncSafeLength += (cID3v2Header[7] & 127) << 14;
39        nSyncSafeLength += (cID3v2Header[8] & 127) << 7;
40        nSyncSafeLength += (cID3v2Header[9] & 127);
41
42        BOOL bHasTagFooter = FALSE;
43
44        if (cID3v2Header[5] & 16)
45        {
46            bHasTagFooter = TRUE;
47            nJunkBytes = nSyncSafeLength + 20;
48        }
49        else
50        {
51            nJunkBytes = nSyncSafeLength + 10;
52        }
53
54        // error check
55        if (cID3v2Header[5] & 64)
56        {
57            // this ID3v2 length calculator algorithm can't cope with extended headers
58            // we should be ok though, because the scan for the MAC header below should
59            // really do the trick
60        }
61
62        m_pIO->Seek(nJunkBytes, FILE_BEGIN);
63
64        // scan for padding (slow and stupid, but who cares here...)
65        if (!bHasTagFooter)
66        {
67            char cTemp = 0;
68            m_pIO->Read((unsigned char *) &cTemp, 1, &nBytesRead);
69            while (cTemp == 0 && nBytesRead == 1)
70            {
71                nJunkBytes++;
72                m_pIO->Read((unsigned char *) &cTemp, 1, &nBytesRead);
73            }
74        }
75    }
76    m_pIO->Seek(nJunkBytes, FILE_BEGIN);
77
78    // scan until we hit the APE_DESCRIPTOR, the end of the file, or 1 MB later
79    unsigned int nGoalID = (' ' << 24) | ('C' << 16) | ('A' << 8) | ('M');
80    unsigned int nReadID = 0;
81    int nRetVal = m_pIO->Read(&nReadID, 4, &nBytesRead);
82    if (nRetVal != 0 || nBytesRead != 4) return ERROR_UNDEFINED;
83
84	// SHINTA -->
85	nBytesRead = 1;
86	int	nScanBytes = 0;
87	while ( (nGoalID != nReadID) && (nBytesRead > 0) && (nScanBytes < (1024*1024)) ) {
88		unsigned char	cTemp[1024];
89		m_pIO->Read(&cTemp, sizeof(cTemp), &nBytesRead);
90		for ( unsigned int i = 0 ; i < sizeof(cTemp) ; i++ ) {
91			nReadID = (((unsigned int)cTemp[i]) << 24) | (nReadID >> 8);
92			nJunkBytes++;
93			if ( nGoalID == nReadID )
94				break;
95		}
96		nScanBytes += sizeof(cTemp);
97	}
98	// <-- SHINTA
99
100    if (nGoalID != nReadID)
101        nJunkBytes = -1;
102
103    // seek to the proper place (depending on result and settings)
104    if (bSeek && (nJunkBytes != -1))
105    {
106        // successfully found the start of the file (seek to it and return)
107        m_pIO->Seek(nJunkBytes, FILE_BEGIN);
108    }
109    else
110    {
111        // restore the original file pointer
112        m_pIO->Seek(nOriginalFileLocation, FILE_BEGIN);
113    }
114
115    return nJunkBytes;
116}
117
118int CAPEHeader::Analyze(APE_FILE_INFO * pInfo)
119{
120    // error check
121    if ((m_pIO == NULL) || (pInfo == NULL))
122        return ERROR_INVALID_PARAMETER;
123
124    // variables
125    unsigned int nBytesRead = 0;
126
127    // find the descriptor
128    pInfo->nJunkHeaderBytes = FindDescriptor(TRUE);
129    if (pInfo->nJunkHeaderBytes < 0)
130        return ERROR_UNDEFINED;
131
132    // read the first 8 bytes of the descriptor (ID and version)
133    APE_COMMON_HEADER CommonHeader; memset(&CommonHeader, 0, sizeof(APE_COMMON_HEADER));
134    m_pIO->Read(&CommonHeader, sizeof(APE_COMMON_HEADER), &nBytesRead);
135
136    // make sure we're at the ID
137    if (CommonHeader.cID[0] != 'M' || CommonHeader.cID[1] != 'A' || CommonHeader.cID[2] != 'C' || CommonHeader.cID[3] != ' ')
138        return ERROR_UNDEFINED;
139
140    int nRetVal = ERROR_UNDEFINED;
141
142    if (CommonHeader.nVersion >= 3980)
143    {
144        // current header format
145        nRetVal = AnalyzeCurrent(pInfo);
146    }
147    else
148    {
149        // legacy support
150        nRetVal = AnalyzeOld(pInfo);
151    }
152
153    return nRetVal;
154}
155
156int CAPEHeader::AnalyzeCurrent(APE_FILE_INFO * pInfo)
157{
158    // variable declares
159    unsigned int nBytesRead = 0;
160    pInfo->spAPEDescriptor.Assign(new APE_DESCRIPTOR); memset(pInfo->spAPEDescriptor, 0, sizeof(APE_DESCRIPTOR));
161    APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
162
163    // read the descriptor
164    m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
165    m_pIO->Read(pInfo->spAPEDescriptor, sizeof(APE_DESCRIPTOR), &nBytesRead);
166
167    if ((pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead) > 0)
168        m_pIO->Seek(pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead, FILE_CURRENT);
169
170    // read the header
171    m_pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
172
173    if ((pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead) > 0)
174        m_pIO->Seek(pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead, FILE_CURRENT);
175
176    // fill the APE info structure
177    pInfo->nVersion                = int(pInfo->spAPEDescriptor->nVersion);
178    pInfo->nCompressionLevel    = int(APEHeader.nCompressionLevel);
179    pInfo->nFormatFlags            = int(APEHeader.nFormatFlags);
180    pInfo->nTotalFrames            = int(APEHeader.nTotalFrames);
181    pInfo->nFinalFrameBlocks    = int(APEHeader.nFinalFrameBlocks);
182    pInfo->nBlocksPerFrame        = int(APEHeader.nBlocksPerFrame);
183    pInfo->nChannels            = int(APEHeader.nChannels);
184    pInfo->nSampleRate            = int(APEHeader.nSampleRate);
185    pInfo->nBitsPerSample        = int(APEHeader.nBitsPerSample);
186    pInfo->nBytesPerSample        = pInfo->nBitsPerSample / 8;
187    pInfo->nBlockAlign            = pInfo->nBytesPerSample * pInfo->nChannels;
188    pInfo->nTotalBlocks            = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames -  1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
189    pInfo->nWAVHeaderBytes        = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : pInfo->spAPEDescriptor->nHeaderDataBytes;
190    pInfo->nWAVTerminatingBytes    = pInfo->spAPEDescriptor->nTerminatingDataBytes;
191    pInfo->nWAVDataBytes        = pInfo->nTotalBlocks * pInfo->nBlockAlign;
192    pInfo->nWAVTotalBytes        = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
193    pInfo->nAPETotalBytes        = m_pIO->GetSize();
194    pInfo->nLengthMS            = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
195    pInfo->nAverageBitrate        = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
196    pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
197    pInfo->nSeekTableElements    = pInfo->spAPEDescriptor->nSeekTableBytes / 4;
198
199    // get the seek tables (really no reason to get the whole thing if there's extra)
200    pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
201    if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
202
203    m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
204
205    // get the wave header
206    if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
207    {
208        pInfo->spWaveHeaderData.Assign(new unsigned char [pInfo->nWAVHeaderBytes], TRUE);
209        if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
210        m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, pInfo->nWAVHeaderBytes, &nBytesRead);
211    }
212
213    return ERROR_SUCCESS;
214}
215
216int CAPEHeader::AnalyzeOld(APE_FILE_INFO * pInfo)
217{
218    // variable declares
219    unsigned int nBytesRead = 0;
220
221    // read the MAC header from the file
222    APE_HEADER_OLD APEHeader;
223    m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
224    m_pIO->Read((unsigned char *) &APEHeader, sizeof(APEHeader), &nBytesRead);
225
226    // fail on 0 length APE files (catches non-finalized APE files)
227    if (APEHeader.nTotalFrames == 0)
228        return ERROR_UNDEFINED;
229
230    int nPeakLevel = -1;
231    if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL)
232        m_pIO->Read((unsigned char *) &nPeakLevel, 4, &nBytesRead);
233
234    if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS)
235        m_pIO->Read((unsigned char *) &pInfo->nSeekTableElements, 4, &nBytesRead);
236    else
237        pInfo->nSeekTableElements = APEHeader.nTotalFrames;
238
239    // fill the APE info structure
240    pInfo->nVersion                = int(APEHeader.nVersion);
241    pInfo->nCompressionLevel    = int(APEHeader.nCompressionLevel);
242    pInfo->nFormatFlags            = int(APEHeader.nFormatFlags);
243    pInfo->nTotalFrames            = int(APEHeader.nTotalFrames);
244    pInfo->nFinalFrameBlocks    = int(APEHeader.nFinalFrameBlocks);
245    pInfo->nBlocksPerFrame        = ((APEHeader.nVersion >= 3900) || ((APEHeader.nVersion >= 3800) && (APEHeader.nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH))) ? 73728 : 9216;
246    if ((APEHeader.nVersion >= 3950)) pInfo->nBlocksPerFrame = 73728 * 4;
247    pInfo->nChannels            = int(APEHeader.nChannels);
248    pInfo->nSampleRate            = int(APEHeader.nSampleRate);
249    pInfo->nBitsPerSample        = (pInfo->nFormatFlags & MAC_FORMAT_FLAG_8_BIT) ? 8 : ((pInfo->nFormatFlags & MAC_FORMAT_FLAG_24_BIT) ? 24 : 16);
250    pInfo->nBytesPerSample        = pInfo->nBitsPerSample / 8;
251    pInfo->nBlockAlign            = pInfo->nBytesPerSample * pInfo->nChannels;
252    pInfo->nTotalBlocks            = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames -  1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
253    pInfo->nWAVHeaderBytes        = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : APEHeader.nHeaderBytes;
254    pInfo->nWAVTerminatingBytes    = int(APEHeader.nTerminatingBytes);
255    pInfo->nWAVDataBytes        = pInfo->nTotalBlocks * pInfo->nBlockAlign;
256    pInfo->nWAVTotalBytes        = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
257    pInfo->nAPETotalBytes        = m_pIO->GetSize();
258    pInfo->nLengthMS            = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
259    pInfo->nAverageBitrate        = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
260    pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
261
262    // get the wave header
263    if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
264    {
265        pInfo->spWaveHeaderData.Assign(new unsigned char [APEHeader.nHeaderBytes], TRUE);
266        if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
267        m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, APEHeader.nHeaderBytes, &nBytesRead);
268    }
269
270    // get the seek tables (really no reason to get the whole thing if there's extra)
271    pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
272    if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
273
274    m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
275
276    if (APEHeader.nVersion <= 3800)
277    {
278        pInfo->spSeekBitTable.Assign(new unsigned char [pInfo->nSeekTableElements], TRUE);
279        if (pInfo->spSeekBitTable == NULL) { return ERROR_UNDEFINED; }
280
281        m_pIO->Read((unsigned char *) pInfo->spSeekBitTable, pInfo->nSeekTableElements, &nBytesRead);
282    }
283
284    return ERROR_SUCCESS;
285}
286