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 *****************************************************************************************/ 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 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 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 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 *****************************************************************************************/ 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 *****************************************************************************************/ 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 *****************************************************************************************/ 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 *****************************************************************************************/ 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 *****************************************************************************************/ 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