xref: /haiku/src/add-ons/media/plugins/ape_reader/MAClib/APEHeader.cpp (revision d56f636bf4acfad3b6fe8e21becbcafc99bfe307)
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 	// 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 
Analyze(APE_FILE_INFO * pInfo)118 int 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 
AnalyzeCurrent(APE_FILE_INFO * pInfo)156 int 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 
AnalyzeOld(APE_FILE_INFO * pInfo)216 int 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