1 #include <algorithm>
2
3 #include "All.h"
4 #include "APEInfo.h"
5 #include "APECompress.h"
6 #include "APEDecompress.h"
7 #include "WAVInputSource.h"
8 #include IO_HEADER_FILE
9 #include "MACProgressHelper.h"
10 #include "GlobalFunctions.h"
11 #include "CharacterHelper.h"
12 #include "MD5.h"
13
14 #define UNMAC_DECODER_OUTPUT_NONE 0
15 #define UNMAC_DECODER_OUTPUT_WAV 1
16 #define UNMAC_DECODER_OUTPUT_APE 2
17
18 #define BLOCKS_PER_DECODE 9216
19
20 int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nOutputMode, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
21
22 /*****************************************************************************************
23 ANSI wrappers
24 *****************************************************************************************/
CompressFile(const str_ansi * pInputFilename,const str_ansi * pOutputFilename,int nCompressionLevel,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)25 int __stdcall CompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
26 {
27 CSmartPtr<str_utf16> spInputFile(GetUTF16FromANSI(pInputFilename), TRUE);
28 CSmartPtr<str_utf16> spOutputFile(GetUTF16FromANSI(pOutputFilename), TRUE);
29 return CompressFileW(spInputFile, spOutputFile, nCompressionLevel, pPercentageDone, ProgressCallback, pKillFlag);
30 }
31
DecompressFile(const str_ansi * pInputFilename,const str_ansi * pOutputFilename,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)32 int __stdcall DecompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
33 {
34 CSmartPtr<str_utf16> spInputFile(GetUTF16FromANSI(pInputFilename), TRUE);
35 CSmartPtr<str_utf16> spOutputFile(GetUTF16FromANSI(pOutputFilename), TRUE);
36 return DecompressFileW(spInputFile, pOutputFilename ? static_cast<str_utf16*>(spOutputFile) : NULL, pPercentageDone, ProgressCallback, pKillFlag);
37 }
38
ConvertFile(const str_ansi * pInputFilename,const str_ansi * pOutputFilename,int nCompressionLevel,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)39 int __stdcall ConvertFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
40 {
41 CSmartPtr<str_utf16> spInputFile(GetUTF16FromANSI(pInputFilename), TRUE);
42 CSmartPtr<str_utf16> spOutputFile(GetUTF16FromANSI(pOutputFilename), TRUE);
43 return ConvertFileW(spInputFile, spOutputFile, nCompressionLevel, pPercentageDone, ProgressCallback, pKillFlag);
44 }
45
VerifyFile(const str_ansi * pInputFilename,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag,BOOL bQuickVerifyIfPossible)46 int __stdcall VerifyFile(const str_ansi * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible)
47 {
48 CSmartPtr<str_utf16> spInputFile(GetUTF16FromANSI(pInputFilename), TRUE);
49 return VerifyFileW(spInputFile, pPercentageDone, ProgressCallback, pKillFlag, FALSE);
50 }
51
52 /*****************************************************************************************
53 Compress file
54 *****************************************************************************************/
CompressFileW(const str_utf16 * pInputFilename,const str_utf16 * pOutputFilename,int nCompressionLevel,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)55 int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
56 {
57 // declare the variables
58 int nFunctionRetVal = ERROR_SUCCESS;
59 WAVEFORMATEX WaveFormatEx;
60 CSmartPtr<CMACProgressHelper> spMACProgressHelper;
61 CSmartPtr<unsigned char> spBuffer;
62 CSmartPtr<IAPECompress> spAPECompress;
63
64 try
65 {
66 // create the input source
67 int nRetVal = ERROR_UNDEFINED;
68 int nAudioBlocks = 0; int nHeaderBytes = 0; int nTerminatingBytes = 0;
69 CSmartPtr<CInputSource> spInputSource(CreateInputSource(pInputFilename, &WaveFormatEx, &nAudioBlocks,
70 &nHeaderBytes, &nTerminatingBytes, &nRetVal));
71
72 if ((spInputSource == NULL) || (nRetVal != ERROR_SUCCESS))
73 throw nRetVal;
74
75 // create the compressor
76 spAPECompress.Assign(CreateIAPECompress());
77 if (spAPECompress == NULL) throw ERROR_UNDEFINED;
78
79 // figure the audio bytes
80 int nAudioBytes = nAudioBlocks * WaveFormatEx.nBlockAlign;
81
82 // start the encoder
83 if (nHeaderBytes > 0) spBuffer.Assign(new unsigned char [nHeaderBytes], TRUE);
84 THROW_ON_ERROR(spInputSource->GetHeaderData(spBuffer.GetPtr()))
85 THROW_ON_ERROR(spAPECompress->Start(pOutputFilename, &WaveFormatEx, nAudioBytes,
86 nCompressionLevel, spBuffer.GetPtr(), nHeaderBytes));
87
88 spBuffer.Delete();
89
90 // set-up the progress
91 spMACProgressHelper.Assign(new CMACProgressHelper(nAudioBytes, pPercentageDone, ProgressCallback, pKillFlag));
92
93 // master loop
94 int nBytesLeft = nAudioBytes;
95
96 while (nBytesLeft > 0)
97 {
98 int nBytesAdded = 0;
99 THROW_ON_ERROR(spAPECompress->AddDataFromInputSource(spInputSource.GetPtr(), nBytesLeft, &nBytesAdded))
100
101 nBytesLeft -= nBytesAdded;
102
103 // update the progress
104 spMACProgressHelper->UpdateProgress(nAudioBytes - nBytesLeft);
105
106 // process the kill flag
107 if (spMACProgressHelper->ProcessKillFlag(TRUE) != ERROR_SUCCESS)
108 throw(ERROR_USER_STOPPED_PROCESSING);
109 }
110
111 // finalize the file
112 if (nTerminatingBytes > 0) spBuffer.Assign(new unsigned char [nTerminatingBytes], TRUE);
113 THROW_ON_ERROR(spInputSource->GetTerminatingData(spBuffer.GetPtr()));
114 THROW_ON_ERROR(spAPECompress->Finish(spBuffer.GetPtr(), nTerminatingBytes, nTerminatingBytes))
115
116 // update the progress to 100%
117 spMACProgressHelper->UpdateProgressComplete();
118 }
119 catch(int nErrorCode)
120 {
121 nFunctionRetVal = (nErrorCode == 0) ? ERROR_UNDEFINED : nErrorCode;
122 }
123 catch(...)
124 {
125 nFunctionRetVal = ERROR_UNDEFINED;
126 }
127
128 // kill the compressor if we failed
129 if ((nFunctionRetVal != 0) && (spAPECompress != NULL))
130 spAPECompress->Kill();
131
132 // return
133 return nFunctionRetVal;
134 }
135
136
137 /*****************************************************************************************
138 Verify file
139 *****************************************************************************************/
VerifyFileW(const str_utf16 * pInputFilename,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag,BOOL bQuickVerifyIfPossible)140 int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible)
141 {
142 // error check the function parameters
143 if (pInputFilename == NULL)
144 {
145 return ERROR_INVALID_FUNCTION_PARAMETER;
146 }
147
148
149 // return value
150 int nRetVal = ERROR_UNDEFINED;
151
152 // see if we can quick verify
153 if (bQuickVerifyIfPossible)
154 {
155 CSmartPtr<IAPEDecompress> spAPEDecompress;
156 try
157 {
158 int nFunctionRetVal = ERROR_SUCCESS;
159
160 spAPEDecompress.Assign(CreateIAPEDecompress(pInputFilename, &nFunctionRetVal));
161 if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal);
162
163 APE_FILE_INFO * pInfo = (APE_FILE_INFO *) spAPEDecompress->GetInfo(APE_INTERNAL_INFO);
164 if ((pInfo->nVersion < 3980) || (pInfo->spAPEDescriptor == NULL))
165 throw(ERROR_UPSUPPORTED_FILE_VERSION);
166 }
167 catch(...)
168 {
169 bQuickVerifyIfPossible = FALSE;
170 }
171 }
172
173 // if we can and should quick verify, then do it
174 if (bQuickVerifyIfPossible)
175 {
176 // variable declares
177 int nFunctionRetVal = ERROR_SUCCESS;
178 unsigned int nBytesRead = 0;
179 CSmartPtr<IAPEDecompress> spAPEDecompress;
180
181 // run the quick verify
182 try
183 {
184 spAPEDecompress.Assign(CreateIAPEDecompress(pInputFilename, &nFunctionRetVal));
185 if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal);
186
187 CMD5Helper MD5Helper;
188
189 CIO * pIO = GET_IO(spAPEDecompress);
190 APE_FILE_INFO * pInfo = (APE_FILE_INFO *) spAPEDecompress->GetInfo(APE_INTERNAL_INFO);
191
192 if ((pInfo->nVersion < 3980) || (pInfo->spAPEDescriptor == NULL))
193 throw(ERROR_UPSUPPORTED_FILE_VERSION);
194
195 int nHead = pInfo->nJunkHeaderBytes + pInfo->spAPEDescriptor->nDescriptorBytes;
196 int nStart = nHead + pInfo->spAPEDescriptor->nHeaderBytes + pInfo->spAPEDescriptor->nSeekTableBytes;
197
198 pIO->Seek(nHead, FILE_BEGIN);
199 int nHeadBytes = nStart - nHead;
200 CSmartPtr<unsigned char> spHeadBuffer(new unsigned char [nHeadBytes], TRUE);
201 if ((pIO->Read(spHeadBuffer, nHeadBytes, &nBytesRead) != ERROR_SUCCESS) || (nHeadBytes != int(nBytesRead)))
202 throw(ERROR_IO_READ);
203
204 int nBytesLeft = pInfo->spAPEDescriptor->nHeaderDataBytes + pInfo->spAPEDescriptor->nAPEFrameDataBytes + pInfo->spAPEDescriptor->nTerminatingDataBytes;
205 CSmartPtr<unsigned char> spBuffer(new unsigned char [16384], TRUE);
206 nBytesRead = 1;
207 while ((nBytesLeft > 0) && (nBytesRead > 0))
208 {
209 int nBytesToRead = min(16384, nBytesLeft);
210 if (pIO->Read(spBuffer, nBytesToRead, &nBytesRead) != ERROR_SUCCESS)
211 throw(ERROR_IO_READ);
212
213 MD5Helper.AddData(spBuffer, nBytesRead);
214 nBytesLeft -= nBytesRead;
215 }
216
217 if (nBytesLeft != 0)
218 throw(ERROR_IO_READ);
219
220 MD5Helper.AddData(spHeadBuffer, nHeadBytes);
221
222 unsigned char cResult[16];
223 MD5Helper.GetResult(cResult);
224
225 if (memcmp(cResult, pInfo->spAPEDescriptor->cFileMD5, 16) != 0)
226 nFunctionRetVal = ERROR_INVALID_CHECKSUM;
227
228 }
229 catch(int nErrorCode)
230 {
231 nFunctionRetVal = (nErrorCode == 0) ? ERROR_UNDEFINED : nErrorCode;
232 }
233 catch(...)
234 {
235 nFunctionRetVal = ERROR_UNDEFINED;
236 }
237
238 // return value
239 nRetVal = nFunctionRetVal;
240 }
241 else
242 {
243 nRetVal = DecompressCore(pInputFilename, NULL, UNMAC_DECODER_OUTPUT_NONE, -1, pPercentageDone, ProgressCallback, pKillFlag);
244 }
245
246
247 return nRetVal;
248 }
249
250 /*****************************************************************************************
251 Decompress file
252 *****************************************************************************************/
DecompressFileW(const str_utf16 * pInputFilename,const str_utf16 * pOutputFilename,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)253 int __stdcall DecompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
254 {
255 if (pOutputFilename == NULL)
256 return VerifyFileW(pInputFilename, pPercentageDone, ProgressCallback, pKillFlag);
257 else
258 return DecompressCore(pInputFilename, pOutputFilename, UNMAC_DECODER_OUTPUT_WAV, -1, pPercentageDone, ProgressCallback, pKillFlag);
259 }
260
261 /*****************************************************************************************
262 Convert file
263 *****************************************************************************************/
ConvertFileW(const str_utf16 * pInputFilename,const str_utf16 * pOutputFilename,int nCompressionLevel,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)264 int __stdcall ConvertFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
265 {
266 return DecompressCore(pInputFilename, pOutputFilename, UNMAC_DECODER_OUTPUT_APE, nCompressionLevel, pPercentageDone, ProgressCallback, pKillFlag);
267 }
268
269 /*****************************************************************************************
270 Decompress a file using the specified output method
271 *****************************************************************************************/
DecompressCore(const str_utf16 * pInputFilename,const str_utf16 * pOutputFilename,int nOutputMode,int nCompressionLevel,int * pPercentageDone,APE_PROGRESS_CALLBACK ProgressCallback,int * pKillFlag)272 int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nOutputMode, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag)
273 {
274 // error check the function parameters
275 if (pInputFilename == NULL)
276 {
277 return ERROR_INVALID_FUNCTION_PARAMETER;
278 }
279
280 // variable declares
281 int nFunctionRetVal = ERROR_SUCCESS;
282 CSmartPtr<IO_CLASS_NAME> spioOutput;
283 CSmartPtr<IAPECompress> spAPECompress;
284 CSmartPtr<IAPEDecompress> spAPEDecompress;
285 CSmartPtr<unsigned char> spTempBuffer;
286 CSmartPtr<CMACProgressHelper> spMACProgressHelper;
287 WAVEFORMATEX wfeInput;
288
289 try
290 {
291 // create the decoder
292 spAPEDecompress.Assign(CreateIAPEDecompress(pInputFilename, &nFunctionRetVal));
293 if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal);
294
295 // get the input format
296 THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeInput))
297
298 // allocate space for the header
299 spTempBuffer.Assign(new unsigned char [spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)], TRUE);
300 if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY);
301
302 // get the header
303 THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)));
304
305 // initialize the output
306 if (nOutputMode == UNMAC_DECODER_OUTPUT_WAV)
307 {
308 // create the file
309 spioOutput.Assign(new IO_CLASS_NAME); THROW_ON_ERROR(spioOutput->Create(pOutputFilename))
310
311 // output the header
312 THROW_ON_ERROR(WriteSafe(spioOutput, spTempBuffer, spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)));
313 }
314 else if (nOutputMode == UNMAC_DECODER_OUTPUT_APE)
315 {
316 // quit if there is nothing to do
317 if (spAPEDecompress->GetInfo(APE_INFO_FILE_VERSION) == MAC_VERSION_NUMBER && spAPEDecompress->GetInfo(APE_INFO_COMPRESSION_LEVEL) == nCompressionLevel)
318 throw(ERROR_SKIPPED);
319
320 // create and start the compressor
321 spAPECompress.Assign(CreateIAPECompress());
322 THROW_ON_ERROR(spAPECompress->Start(pOutputFilename, &wfeInput, spAPEDecompress->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) * spAPEDecompress->GetInfo(APE_INFO_BLOCK_ALIGN),
323 nCompressionLevel, spTempBuffer, spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)))
324 }
325
326 // allocate space for decompression
327 spTempBuffer.Assign(new unsigned char [spAPEDecompress->GetInfo(APE_INFO_BLOCK_ALIGN) * BLOCKS_PER_DECODE], TRUE);
328 if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY);
329
330 int nBlocksLeft = spAPEDecompress->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS);
331
332 // create the progress helper
333 spMACProgressHelper.Assign(new CMACProgressHelper(nBlocksLeft / BLOCKS_PER_DECODE, pPercentageDone, ProgressCallback, pKillFlag));
334
335 // main decoding loop
336 while (nBlocksLeft > 0)
337 {
338 // decode data
339 int nBlocksDecoded = -1;
340 int nRetVal = spAPEDecompress->GetData((char *) spTempBuffer.GetPtr(), BLOCKS_PER_DECODE, &nBlocksDecoded);
341 if (nRetVal != ERROR_SUCCESS)
342 throw(ERROR_INVALID_CHECKSUM);
343
344 // handle the output
345 if (nOutputMode == UNMAC_DECODER_OUTPUT_WAV)
346 {
347 unsigned int nBytesToWrite = (nBlocksDecoded * spAPEDecompress->GetInfo(APE_INFO_BLOCK_ALIGN));
348 unsigned int nBytesWritten = 0;
349 int nRetVal = spioOutput->Write(spTempBuffer, nBytesToWrite, &nBytesWritten);
350 if ((nRetVal != 0) || (nBytesToWrite != nBytesWritten))
351 throw(ERROR_IO_WRITE);
352 }
353 else if (nOutputMode == UNMAC_DECODER_OUTPUT_APE)
354 {
355 THROW_ON_ERROR(spAPECompress->AddData(spTempBuffer, nBlocksDecoded * spAPEDecompress->GetInfo(APE_INFO_BLOCK_ALIGN)))
356 }
357
358 // update amount remaining
359 nBlocksLeft -= nBlocksDecoded;
360
361 // update progress and kill flag
362 spMACProgressHelper->UpdateProgress();
363 if (spMACProgressHelper->ProcessKillFlag(TRUE) != 0)
364 throw(ERROR_USER_STOPPED_PROCESSING);
365 }
366
367 // terminate the output
368 if (nOutputMode == UNMAC_DECODER_OUTPUT_WAV)
369 {
370 // write any terminating WAV data
371 if (spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES) > 0)
372 {
373 spTempBuffer.Assign(new unsigned char[spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)], TRUE);
374 if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY);
375 THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)))
376
377 unsigned int nBytesToWrite = spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES);
378 unsigned int nBytesWritten = 0;
379 int nRetVal = spioOutput->Write(spTempBuffer, nBytesToWrite, &nBytesWritten);
380 if ((nRetVal != 0) || (nBytesToWrite != nBytesWritten))
381 throw(ERROR_IO_WRITE);
382 }
383 }
384 else if (nOutputMode == UNMAC_DECODER_OUTPUT_APE)
385 {
386 // write the WAV data and any tag
387 int nTagBytes = GET_TAG(spAPEDecompress)->GetTagBytes();
388 BOOL bHasTag = (nTagBytes > 0);
389 int nTerminatingBytes = nTagBytes;
390 nTerminatingBytes += spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES);
391
392 if (nTerminatingBytes > 0)
393 {
394 spTempBuffer.Assign(new unsigned char[nTerminatingBytes], TRUE);
395 if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY);
396
397 THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), nTerminatingBytes))
398
399 if (bHasTag)
400 {
401 unsigned int nBytesRead = 0;
402 THROW_ON_ERROR(GET_IO(spAPEDecompress)->Seek(-(nTagBytes), FILE_END))
403 THROW_ON_ERROR(GET_IO(spAPEDecompress)->Read(&spTempBuffer[spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)], nTagBytes, &nBytesRead))
404 }
405
406 THROW_ON_ERROR(spAPECompress->Finish(spTempBuffer, nTerminatingBytes, spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)));
407 }
408 else
409 {
410 THROW_ON_ERROR(spAPECompress->Finish(NULL, 0, 0));
411 }
412 }
413
414 // fire the "complete" progress notification
415 spMACProgressHelper->UpdateProgressComplete();
416 }
417 catch(int nErrorCode)
418 {
419 nFunctionRetVal = (nErrorCode == 0) ? ERROR_UNDEFINED : nErrorCode;
420 }
421 catch(...)
422 {
423 nFunctionRetVal = ERROR_UNDEFINED;
424 }
425
426 // return
427 return nFunctionRetVal;
428 }
429