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 
CAPEHeader(CIO * pIO)8 CAPEHeader::CAPEHeader(CIO * pIO)
9 {
10     m_pIO = pIO;
11 }
12 
~CAPEHeader()13 CAPEHeader::~CAPEHeader()
14 {
15 
16 }
17 
FindDescriptor(BOOL bSeek)18 int 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     nBytesRead = 1;
85     int nScanBytes = 0;
86     while ((nGoalID != swap_int32(nReadID)) && (nBytesRead == 1) && (nScanBytes < (1024 * 1024)))
87     {
88         unsigned char cTemp;
89         m_pIO->Read(&cTemp, 1, &nBytesRead);
90         nReadID = (((unsigned int) cTemp) << 24) | (nReadID >> 8);
91         nJunkBytes++;
92         nScanBytes++;
93     }
94 
95     nReadID = swap_int32(nReadID);
96     if (nGoalID != nReadID)
97         nJunkBytes = -1;
98 
99     // seek to the proper place (depending on result and settings)
100     if (bSeek && (nJunkBytes != -1))
101     {
102         // successfully found the start of the file (seek to it and return)
103         m_pIO->Seek(nJunkBytes, FILE_BEGIN);
104     }
105     else
106     {
107         // restore the original file pointer
108         m_pIO->Seek(nOriginalFileLocation, FILE_BEGIN);
109     }
110 
111     return nJunkBytes;
112 }
113 
Analyze(APE_FILE_INFO * pInfo)114 int CAPEHeader::Analyze(APE_FILE_INFO * pInfo)
115 {
116     // error check
117     if ((m_pIO == NULL) || (pInfo == NULL))
118         return ERROR_INVALID_FUNCTION_PARAMETER;
119 
120     // variables
121     unsigned int nBytesRead = 0;
122 
123     // find the descriptor
124     pInfo->nJunkHeaderBytes = FindDescriptor(TRUE);
125     if (pInfo->nJunkHeaderBytes < 0)
126         return ERROR_UNDEFINED;
127 
128     // read the first 8 bytes of the descriptor (ID and version)
129     APE_COMMON_HEADER CommonHeader; memset(&CommonHeader, 0, sizeof(APE_COMMON_HEADER));
130     m_pIO->Read(&CommonHeader, sizeof(APE_COMMON_HEADER), &nBytesRead);
131     CommonHeader.nVersion = swap_int16(CommonHeader.nVersion);
132 
133     // make sure we're at the ID
134     if (CommonHeader.cID[0] != 'M' || CommonHeader.cID[1] != 'A' || CommonHeader.cID[2] != 'C' || CommonHeader.cID[3] != ' ')
135         return ERROR_UNDEFINED;
136 
137     int nRetVal = ERROR_UNDEFINED;
138 
139     if (CommonHeader.nVersion >= 3980)
140     {
141         // current header format
142         nRetVal = AnalyzeCurrent(pInfo);
143     }
144     else
145     {
146         // legacy support
147         nRetVal = AnalyzeOld(pInfo);
148     }
149 
150     return nRetVal;
151 }
152 
153 #ifdef WORDS_BIGENDIAN
154 
swap_ape_header_old(APE_HEADER_OLD * header)155 void swap_ape_header_old(APE_HEADER_OLD *header)
156 {
157     header->nVersion = swap_int16(header->nVersion);
158     header->nCompressionLevel = swap_int16(header->nCompressionLevel);
159     header->nFormatFlags = swap_int16(header->nFormatFlags);
160 
161     header->nChannels = swap_int16(header->nChannels);
162     header->nSampleRate = swap_int32(header->nSampleRate);
163 
164     header->nHeaderBytes = swap_int32(header->nHeaderBytes);
165     header->nTerminatingBytes = swap_int32(header->nTerminatingBytes);
166     header->nTotalFrames = swap_int32(header->nTotalFrames);
167     header->nFinalFrameBlocks = swap_int32(header->nFinalFrameBlocks);
168 }
169 
170 #else
171 
172 #define swap_ape_header_old(a) {}
173 
174 #endif
175 
AnalyzeCurrent(APE_FILE_INFO * pInfo)176 int CAPEHeader::AnalyzeCurrent(APE_FILE_INFO * pInfo)
177 {
178     // variable declares
179     unsigned int nBytesRead = 0;
180     pInfo->spAPEDescriptor.Assign(new APE_DESCRIPTOR); memset(pInfo->spAPEDescriptor, 0, sizeof(APE_DESCRIPTOR));
181     APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
182 
183     // read the descriptor
184     m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
185     m_pIO->Read(pInfo->spAPEDescriptor, sizeof(APE_DESCRIPTOR), &nBytesRead);
186     swap_ape_descriptor((APE_DESCRIPTOR *)pInfo->spAPEDescriptor);
187 
188     if ((pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead) > 0)
189         m_pIO->Seek(pInfo->spAPEDescriptor->nDescriptorBytes - nBytesRead, FILE_CURRENT);
190 
191     // read the header
192     m_pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
193     swap_ape_header(&APEHeader);
194 
195     if ((pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead) > 0)
196         m_pIO->Seek(pInfo->spAPEDescriptor->nHeaderBytes - nBytesRead, FILE_CURRENT);
197 
198     // fill the APE info structure
199     pInfo->nVersion                = int(pInfo->spAPEDescriptor->nVersion);
200     pInfo->nCompressionLevel    = int(APEHeader.nCompressionLevel);
201     pInfo->nFormatFlags            = int(APEHeader.nFormatFlags);
202     pInfo->nTotalFrames            = int(APEHeader.nTotalFrames);
203     pInfo->nFinalFrameBlocks    = int(APEHeader.nFinalFrameBlocks);
204     pInfo->nBlocksPerFrame        = int(APEHeader.nBlocksPerFrame);
205     pInfo->nChannels            = int(APEHeader.nChannels);
206     pInfo->nSampleRate            = int(APEHeader.nSampleRate);
207     pInfo->nBitsPerSample        = int(APEHeader.nBitsPerSample);
208     pInfo->nBytesPerSample        = pInfo->nBitsPerSample / 8;
209     pInfo->nBlockAlign            = pInfo->nBytesPerSample * pInfo->nChannels;
210     pInfo->nTotalBlocks            = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames -  1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
211     pInfo->nWAVHeaderBytes        = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : pInfo->spAPEDescriptor->nHeaderDataBytes;
212     pInfo->nWAVTerminatingBytes    = pInfo->spAPEDescriptor->nTerminatingDataBytes;
213     pInfo->nWAVDataBytes        = pInfo->nTotalBlocks * pInfo->nBlockAlign;
214     pInfo->nWAVTotalBytes        = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
215     pInfo->nAPETotalBytes        = m_pIO->GetSize();
216     pInfo->nLengthMS            = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
217     pInfo->nAverageBitrate        = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
218     pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
219     pInfo->nSeekTableElements    = pInfo->spAPEDescriptor->nSeekTableBytes / 4;
220 
221     // get the seek tables (really no reason to get the whole thing if there's extra)
222     pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
223     if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
224 
225     m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
226 
227 #ifdef WORDS_BIGENDIAN
228 
229     uint32 *ptr = pInfo->spSeekByteTable.GetPtr();
230     int i = 0;
231     for (i = 0; i < pInfo->nSeekTableElements; i ++)
232     {
233 	ptr[i] = swap_int32(ptr[i]);
234     }
235 
236 #endif
237 
238     // get the wave header
239     if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
240     {
241         pInfo->spWaveHeaderData.Assign(new unsigned char [pInfo->nWAVHeaderBytes], TRUE);
242         if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
243         m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, pInfo->nWAVHeaderBytes, &nBytesRead);
244     }
245 
246     return ERROR_SUCCESS;
247 }
248 
AnalyzeOld(APE_FILE_INFO * pInfo)249 int CAPEHeader::AnalyzeOld(APE_FILE_INFO * pInfo)
250 {
251     // variable declares
252     unsigned int nBytesRead = 0;
253 
254     // read the MAC header from the file
255     APE_HEADER_OLD APEHeader;
256     m_pIO->Seek(pInfo->nJunkHeaderBytes, FILE_BEGIN);
257     m_pIO->Read((unsigned char *) &APEHeader, sizeof(APEHeader), &nBytesRead);
258     swap_ape_header_old(&APEHeader);
259 
260     // fail on 0 length APE files (catches non-finalized APE files)
261     if (APEHeader.nTotalFrames == 0)
262         return ERROR_UNDEFINED;
263 
264     int nPeakLevel = -1;
265     if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL)
266     {
267         m_pIO->Read((unsigned char *) &nPeakLevel, 4, &nBytesRead);
268 	nPeakLevel = swap_int32(nPeakLevel);
269     }
270 
271     if (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS)
272     {
273         m_pIO->Read((unsigned char *) &pInfo->nSeekTableElements, 4, &nBytesRead);
274 	pInfo->nSeekTableElements = swap_int32(pInfo->nSeekTableElements);
275     }
276     else
277         pInfo->nSeekTableElements = APEHeader.nTotalFrames;
278 
279     // fill the APE info structure
280     pInfo->nVersion                = int(APEHeader.nVersion);
281     pInfo->nCompressionLevel    = int(APEHeader.nCompressionLevel);
282     pInfo->nFormatFlags            = int(APEHeader.nFormatFlags);
283     pInfo->nTotalFrames            = int(APEHeader.nTotalFrames);
284     pInfo->nFinalFrameBlocks    = int(APEHeader.nFinalFrameBlocks);
285     pInfo->nBlocksPerFrame        = ((APEHeader.nVersion >= 3900) || ((APEHeader.nVersion >= 3800) && (APEHeader.nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH))) ? 73728 : 9216;
286     if ((APEHeader.nVersion >= 3950)) pInfo->nBlocksPerFrame = 73728 * 4;
287     pInfo->nChannels            = int(APEHeader.nChannels);
288     pInfo->nSampleRate            = int(APEHeader.nSampleRate);
289     pInfo->nBitsPerSample        = (pInfo->nFormatFlags & MAC_FORMAT_FLAG_8_BIT) ? 8 : ((pInfo->nFormatFlags & MAC_FORMAT_FLAG_24_BIT) ? 24 : 16);
290     pInfo->nBytesPerSample        = pInfo->nBitsPerSample / 8;
291     pInfo->nBlockAlign            = pInfo->nBytesPerSample * pInfo->nChannels;
292     pInfo->nTotalBlocks            = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames -  1) * pInfo->nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
293     pInfo->nWAVHeaderBytes        = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : APEHeader.nHeaderBytes;
294     pInfo->nWAVTerminatingBytes    = int(APEHeader.nTerminatingBytes);
295     pInfo->nWAVDataBytes        = pInfo->nTotalBlocks * pInfo->nBlockAlign;
296     pInfo->nWAVTotalBytes        = pInfo->nWAVDataBytes + pInfo->nWAVHeaderBytes + pInfo->nWAVTerminatingBytes;
297     pInfo->nAPETotalBytes        = m_pIO->GetSize();
298     pInfo->nLengthMS            = int((double(pInfo->nTotalBlocks) * double(1000)) / double(pInfo->nSampleRate));
299     pInfo->nAverageBitrate        = (pInfo->nLengthMS <= 0) ? 0 : int((double(pInfo->nAPETotalBytes) * double(8)) / double(pInfo->nLengthMS));
300     pInfo->nDecompressedBitrate = (pInfo->nBlockAlign * pInfo->nSampleRate * 8) / 1000;
301 
302     // get the wave header
303     if (!(APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER))
304     {
305         pInfo->spWaveHeaderData.Assign(new unsigned char [APEHeader.nHeaderBytes], TRUE);
306         if (pInfo->spWaveHeaderData == NULL) { return ERROR_UNDEFINED; }
307         m_pIO->Read((unsigned char *) pInfo->spWaveHeaderData, APEHeader.nHeaderBytes, &nBytesRead);
308     }
309 
310     // get the seek tables (really no reason to get the whole thing if there's extra)
311     pInfo->spSeekByteTable.Assign(new uint32 [pInfo->nSeekTableElements], TRUE);
312     if (pInfo->spSeekByteTable == NULL) { return ERROR_UNDEFINED; }
313 
314     m_pIO->Read((unsigned char *) pInfo->spSeekByteTable.GetPtr(), 4 * pInfo->nSeekTableElements, &nBytesRead);
315 
316 #ifdef WORDS_BIGENDIAN
317 
318     uint32 *ptr = pInfo->spSeekByteTable.GetPtr();
319     int i = 0;
320     for (i = 0; i < pInfo->nSeekTableElements; i ++)
321     {
322 	ptr[i] = swap_int32(ptr[i]);
323     }
324 
325 #endif
326 
327     if (APEHeader.nVersion <= 3800)
328     {
329         pInfo->spSeekBitTable.Assign(new unsigned char [pInfo->nSeekTableElements], TRUE);
330         if (pInfo->spSeekBitTable == NULL) { return ERROR_UNDEFINED; }
331 
332         m_pIO->Read((unsigned char *) pInfo->spSeekBitTable, pInfo->nSeekTableElements, &nBytesRead);
333     }
334 
335     return ERROR_SUCCESS;
336 }
337