1/*****************************************************************************************
2CAPEInfo:
3    -a class to make working with APE files and getting information about them simple
4*****************************************************************************************/
5#include "All.h"
6#include "APEInfo.h"
7#include IO_HEADER_FILE
8#include "APECompress.h"
9#include "APEHeader.h"
10
11/*****************************************************************************************
12Construction
13*****************************************************************************************/
14CAPEInfo::CAPEInfo(int * pErrorCode, const char* pFilename, CAPETag * pTag)
15{
16    *pErrorCode = ERROR_SUCCESS;
17    CloseFile();
18
19    // open the file
20    m_spIO.Assign(new IO_CLASS_NAME);
21
22    if (m_spIO->Open(pFilename) != 0)
23    {
24        CloseFile();
25        *pErrorCode = ERROR_INVALID_INPUT_FILE;
26        return;
27    }
28
29    // get the file information
30    if (GetFileInformation(TRUE) != 0)
31    {
32        CloseFile();
33        *pErrorCode = ERROR_INVALID_INPUT_FILE;
34        return;
35    }
36
37    // get the tag (do this second so that we don't do it on failure)
38    if (pTag == NULL)
39    {
40        // we don't want to analyze right away for non-local files
41        // since a single I/O object is shared, we can't tag and read at the same time (i.e. in multiple threads)
42        BOOL bAnalyzeNow = TRUE;
43        if ((strncmp(pFilename, "http://", 7) == 0) || (strncmp(pFilename, "m01p://", 7) == 0))		// SHINTA: wchar_t -> char
44            bAnalyzeNow = FALSE;
45
46        m_spAPETag.Assign(new CAPETag(m_spIO, bAnalyzeNow));
47    }
48    else
49    {
50        m_spAPETag.Assign(pTag);
51    }
52
53}
54
55CAPEInfo::CAPEInfo(int * pErrorCode, CIO * pIO, CAPETag * pTag)
56{
57    *pErrorCode = ERROR_SUCCESS;
58    CloseFile();
59
60    m_spIO.Assign(pIO, FALSE, FALSE);
61
62    // get the file information
63    if (GetFileInformation(TRUE) != 0)
64    {
65        CloseFile();
66        *pErrorCode = ERROR_INVALID_INPUT_FILE;
67        return;
68    }
69
70    // get the tag (do this second so that we don't do it on failure)
71    if (pTag == NULL)
72        m_spAPETag.Assign(new CAPETag(m_spIO, TRUE));
73    else
74        m_spAPETag.Assign(pTag);
75}
76
77
78/*****************************************************************************************
79Destruction
80*****************************************************************************************/
81CAPEInfo::~CAPEInfo()
82{
83    CloseFile();
84}
85
86/*****************************************************************************************
87Close the file
88*****************************************************************************************/
89int CAPEInfo::CloseFile()
90{
91    m_spIO.Delete();
92    m_APEFileInfo.spWaveHeaderData.Delete();
93    m_APEFileInfo.spSeekBitTable.Delete();
94    m_APEFileInfo.spSeekByteTable.Delete();
95    m_APEFileInfo.spAPEDescriptor.Delete();
96
97    m_spAPETag.Delete();
98
99    // re-initialize variables
100    m_APEFileInfo.nSeekTableElements = 0;
101    m_bHasFileInformationLoaded = FALSE;
102
103    return ERROR_SUCCESS;
104}
105
106/*****************************************************************************************
107Get the file information about the file
108*****************************************************************************************/
109int CAPEInfo::GetFileInformation(BOOL bGetTagInformation)
110{
111    // quit if there is no simple file
112    if (m_spIO == NULL) { return -1; }
113
114    // quit if the file information has already been loaded
115    if (m_bHasFileInformationLoaded) { return ERROR_SUCCESS; }
116
117    // use a CAPEHeader class to help us analyze the file
118    CAPEHeader APEHeader(m_spIO);
119    int nRetVal = APEHeader.Analyze(&m_APEFileInfo);
120
121    // update our internal state
122    if (nRetVal == ERROR_SUCCESS)
123        m_bHasFileInformationLoaded = TRUE;
124
125    // return
126    return nRetVal;
127}
128
129/*****************************************************************************************
130Primary query function
131*****************************************************************************************/
132int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2)
133{
134    int nRetVal = -1;
135
136    switch (Field)
137    {
138    case APE_INFO_FILE_VERSION:
139        nRetVal = m_APEFileInfo.nVersion;
140        break;
141    case APE_INFO_COMPRESSION_LEVEL:
142        nRetVal = m_APEFileInfo.nCompressionLevel;
143        break;
144    case APE_INFO_FORMAT_FLAGS:
145        nRetVal = m_APEFileInfo.nFormatFlags;
146        break;
147    case APE_INFO_SAMPLE_RATE:
148        nRetVal = m_APEFileInfo.nSampleRate;
149        break;
150    case APE_INFO_BITS_PER_SAMPLE:
151        nRetVal = m_APEFileInfo.nBitsPerSample;
152        break;
153    case APE_INFO_BYTES_PER_SAMPLE:
154        nRetVal = m_APEFileInfo.nBytesPerSample;
155        break;
156    case APE_INFO_CHANNELS:
157        nRetVal = m_APEFileInfo.nChannels;
158        break;
159    case APE_INFO_BLOCK_ALIGN:
160        nRetVal = m_APEFileInfo.nBlockAlign;
161        break;
162    case APE_INFO_BLOCKS_PER_FRAME:
163        nRetVal = m_APEFileInfo.nBlocksPerFrame;
164        break;
165    case APE_INFO_FINAL_FRAME_BLOCKS:
166        nRetVal = m_APEFileInfo.nFinalFrameBlocks;
167        break;
168    case APE_INFO_TOTAL_FRAMES:
169        nRetVal = m_APEFileInfo.nTotalFrames;
170        break;
171    case APE_INFO_WAV_HEADER_BYTES:
172        nRetVal = m_APEFileInfo.nWAVHeaderBytes;
173        break;
174    case APE_INFO_WAV_TERMINATING_BYTES:
175        nRetVal = m_APEFileInfo.nWAVTerminatingBytes;
176        break;
177    case APE_INFO_WAV_DATA_BYTES:
178        nRetVal = m_APEFileInfo.nWAVDataBytes;
179        break;
180    case APE_INFO_WAV_TOTAL_BYTES:
181        nRetVal = m_APEFileInfo.nWAVTotalBytes;
182        break;
183    case APE_INFO_APE_TOTAL_BYTES:
184        nRetVal = m_APEFileInfo.nAPETotalBytes;
185        break;
186    case APE_INFO_TOTAL_BLOCKS:
187        nRetVal = m_APEFileInfo.nTotalBlocks;
188        break;
189    case APE_INFO_LENGTH_MS:
190        nRetVal = m_APEFileInfo.nLengthMS;
191        break;
192    case APE_INFO_AVERAGE_BITRATE:
193        nRetVal = m_APEFileInfo.nAverageBitrate;
194        break;
195    case APE_INFO_FRAME_BITRATE:
196    {
197        int nFrame = nParam1;
198
199        nRetVal = 0;
200
201        int nFrameBytes = GetInfo(APE_INFO_FRAME_BYTES, nFrame);
202        int nFrameBlocks = GetInfo(APE_INFO_FRAME_BLOCKS, nFrame);
203        if ((nFrameBytes > 0) && (nFrameBlocks > 0) && m_APEFileInfo.nSampleRate > 0)
204        {
205            int nFrameMS = (nFrameBlocks * 1000) / m_APEFileInfo.nSampleRate;
206            if (nFrameMS != 0)
207            {
208                nRetVal = (nFrameBytes * 8) / nFrameMS;
209            }
210        }
211        break;
212    }
213    case APE_INFO_DECOMPRESSED_BITRATE:
214        nRetVal = m_APEFileInfo.nDecompressedBitrate;
215        break;
216    case APE_INFO_PEAK_LEVEL:
217        nRetVal = -1; // no longer supported
218        break;
219    case APE_INFO_SEEK_BIT:
220    {
221        int nFrame = nParam1;
222        if (GET_FRAMES_START_ON_BYTES_BOUNDARIES(this))
223        {
224            nRetVal = 0;
225        }
226        else
227        {
228            if (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames)
229                nRetVal = 0;
230            else
231                nRetVal = m_APEFileInfo.spSeekBitTable[nFrame];
232        }
233        break;
234    }
235    case APE_INFO_SEEK_BYTE:
236    {
237        int nFrame = nParam1;
238        if (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames)
239            nRetVal = 0;
240        else
241            nRetVal = m_APEFileInfo.spSeekByteTable[nFrame] + m_APEFileInfo.nJunkHeaderBytes;
242        break;
243    }
244    case APE_INFO_WAV_HEADER_DATA:
245    {
246        char * pBuffer = (char *) nParam1;
247        int nMaxBytes = nParam2;
248
249        if (m_APEFileInfo.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)
250        {
251            if (sizeof(WAVE_HEADER) > static_cast<uint32>(nMaxBytes))
252            {
253                nRetVal = -1;
254            }
255            else
256            {
257                WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0);
258                WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, m_APEFileInfo.nWAVDataBytes, &wfeFormat,
259                    m_APEFileInfo.nWAVTerminatingBytes);
260                memcpy(pBuffer, &WAVHeader, sizeof(WAVE_HEADER));
261                nRetVal = 0;
262            }
263        }
264        else
265        {
266            if (m_APEFileInfo.nWAVHeaderBytes > nMaxBytes)
267            {
268                nRetVal = -1;
269            }
270            else
271            {
272                memcpy(pBuffer, m_APEFileInfo.spWaveHeaderData, m_APEFileInfo.nWAVHeaderBytes);
273                nRetVal = 0;
274            }
275        }
276        break;
277    }
278    case APE_INFO_WAV_TERMINATING_DATA:
279    {
280        char * pBuffer = (char *) nParam1;
281        int nMaxBytes = nParam2;
282
283        if (m_APEFileInfo.nWAVTerminatingBytes > nMaxBytes)
284        {
285            nRetVal = -1;
286        }
287        else
288        {
289            if (m_APEFileInfo.nWAVTerminatingBytes > 0)
290            {
291                // variables
292                int nOriginalFileLocation = m_spIO->GetPosition();
293                unsigned int nBytesRead = 0;
294
295                // check for a tag
296                m_spIO->Seek(-(m_spAPETag->GetTagBytes() + m_APEFileInfo.nWAVTerminatingBytes), FILE_END);
297                m_spIO->Read(pBuffer, m_APEFileInfo.nWAVTerminatingBytes, &nBytesRead);
298
299                // restore the file pointer
300                m_spIO->Seek(nOriginalFileLocation, FILE_BEGIN);
301            }
302            nRetVal = 0;
303        }
304        break;
305    }
306    case APE_INFO_WAVEFORMATEX:
307    {
308        WAVEFORMATEX * pWaveFormatEx = (WAVEFORMATEX *) nParam1;
309        FillWaveFormatEx(pWaveFormatEx, m_APEFileInfo.nSampleRate, m_APEFileInfo.nBitsPerSample, m_APEFileInfo.nChannels);
310        nRetVal = 0;
311        break;
312    }
313    case APE_INFO_IO_SOURCE:
314        nRetVal = (int) m_spIO.GetPtr();
315        break;
316    case APE_INFO_FRAME_BYTES:
317    {
318        int nFrame = nParam1;
319
320        // bound-check the frame index
321        if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
322        {
323            nRetVal = -1;
324        }
325        else
326        {
327            if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
328                nRetVal = GetInfo(APE_INFO_SEEK_BYTE, nFrame + 1) - GetInfo(APE_INFO_SEEK_BYTE, nFrame);
329            else
330                nRetVal = m_spIO->GetSize() - m_spAPETag->GetTagBytes() - m_APEFileInfo.nWAVTerminatingBytes - GetInfo(APE_INFO_SEEK_BYTE, nFrame);
331        }
332        break;
333    }
334    case APE_INFO_FRAME_BLOCKS:
335    {
336        int nFrame = nParam1;
337
338        // bound-check the frame index
339        if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
340        {
341            nRetVal = -1;
342        }
343        else
344        {
345            if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
346                nRetVal = m_APEFileInfo.nBlocksPerFrame;
347            else
348                nRetVal = m_APEFileInfo.nFinalFrameBlocks;
349        }
350        break;
351    }
352    case APE_INFO_TAG:
353        nRetVal = (int) m_spAPETag.GetPtr();
354        break;
355    case APE_INTERNAL_INFO:
356        nRetVal = (int) &m_APEFileInfo;
357        break;
358    default:
359    	nRetVal=0;
360    }
361
362    return nRetVal;
363}
364