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