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