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