diff -urN mac-3.99-u4-b5/ChangeLog.shntool mac-3.99-u4-b5-s4/ChangeLog.shntool --- mac-3.99-u4-b5/ChangeLog.shntool 1969-12-31 19:00:00.000000000 -0500 +++ mac-3.99-u4-b5-s4/ChangeLog.shntool 2008-03-12 11:32:44.000000000 -0400 @@ -0,0 +1,16 @@ +version 3.99-u4-b5-s4 + + include error reason with error code in mac.exe + + automatically recalculate quick verify checksum when compressing, if + necessary (e.g. piped encoding with foobar2000, or truncated WAVE file) + +version 3.99-u4-b5-s3 + + reenabled quick-verification of APE files (via new '-q' option) + +version 3.99-u4-b5-s2 + + show above version in mac.exe help screen (sN = shntool revision N) + + added support for encoding WAVE data of unknown length (all files/pipes + are now treated as unknown length; this forces mac.exe to write a seek + table of maximum size regardless of input file size, adding ~30kb/file) + +version 3.99-u4-b5-s1 + + added support for pipe encoding/decoding diff -urN mac-3.99-u4-b5/src/Console/Console.cpp mac-3.99-u4-b5-s4/src/Console/Console.cpp --- mac-3.99-u4-b5/src/Console/Console.cpp 2006-06-01 05:00:55.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/Console/Console.cpp 2008-03-12 11:50:48.000000000 -0400 @@ -22,6 +22,21 @@ // global variables TICK_COUNT_TYPE g_nInitialTickCount = 0; +#ifdef SHNTOOL +BOOL bQuickVerify = FALSE; + +typedef struct +{ + int nErrorNum; + char *sErrorString; +} _ErrorDesc; + +_ErrorDesc ErrorList[][2] = { + ERROR_EXPLANATION + NULL +}; +#endif + /*************************************************************************************** Displays the proper usage for MAC.exe ***************************************************************************************/ @@ -37,12 +52,18 @@ fprintf(pFile, " Compress (insane): '-c5000'\n"); fprintf(pFile, " Decompress: '-d'\n"); fprintf(pFile, " Verify: '-v'\n"); +#ifdef SHNTOOL + fprintf(pFile, " Quick verify: '-q'\n"); +#endif fprintf(pFile, " Convert: '-nXXXX'\n\n"); fprintf(pFile, "Examples:\n"); fprintf(pFile, " Compress: mac.exe \"Metallica - One.wav\" \"Metallica - One.ape\" -c2000\n"); fprintf(pFile, " Decompress: mac.exe \"Metallica - One.ape\" \"Metallica - One.wav\" -d\n"); fprintf(pFile, " Verify: mac.exe \"Metallica - One.ape\" -v\n"); +#ifdef SHNTOOL + fprintf(pFile, " Quick verify: mac.exe \"Metallica - One.ape\" -q\n"); +#endif fprintf(pFile, " (note: int filenames must be put inside of quotations)\n"); } @@ -65,6 +86,22 @@ dProgress * 100, dRemaining, dElapsed); } +#ifdef SHNTOOL +char *ErrorToString(int nErrNo) +{ + int i = 0; + + while (ErrorList[i]) + { + if (ErrorList[i]->nErrorNum == nErrNo) + return ErrorList[i]->sErrorString; + i++; + } + + return "unknown error"; +} +#endif + /*************************************************************************************** Main (the main function) ***************************************************************************************/ @@ -104,7 +141,11 @@ char cMode[256]; strcpy(cMode, argv[2]); +#ifdef SHNTOOL + if (_strnicmp(cMode, "-v", 2) != 0 && _strnicmp(cMode, "-q", 2) != 0) +#else if (_strnicmp(cMode, "-v", 2) != 0) +#endif { // verify is the only mode that doesn't use at least the third argument if (argc < 4) @@ -124,7 +165,19 @@ else if (_strnicmp(cMode, "-d", 2) == 0) nMode = DECOMPRESS_MODE; else if (_strnicmp(cMode, "-v", 2) == 0) +#ifdef SHNTOOL + { + nMode = VERIFY_MODE; + bQuickVerify = FALSE; + } + else if (_strnicmp(cMode, "-q", 2) == 0) + { + nMode = VERIFY_MODE; + bQuickVerify = TRUE; + } +#else nMode = VERIFY_MODE; +#endif else if (_strnicmp(cMode, "-n", 2) == 0) nMode = CONVERT_MODE; @@ -173,7 +226,11 @@ else if (nMode == VERIFY_MODE) { fprintf(stderr, "Verifying...\n"); +#ifdef SHNTOOL + nRetVal = VerifyFileW(spInputFilename, &nPercentageDone, ProgressCallback, &nKillFlag, bQuickVerify); +#else nRetVal = VerifyFileW(spInputFilename, &nPercentageDone, ProgressCallback, &nKillFlag); +#endif } else if (nMode == CONVERT_MODE) { @@ -184,7 +241,11 @@ if (nRetVal == ERROR_SUCCESS) fprintf(stderr, "\nSuccess...\n"); else +#ifdef SHNTOOL + fprintf(stderr, "\nError: %i (%s)\n", nRetVal, ErrorToString(nRetVal)); +#else fprintf(stderr, "\nError: %i\n", nRetVal); +#endif return nRetVal; } diff -urN mac-3.99-u4-b5/src/MACLib/APECompress.cpp mac-3.99-u4-b5-s4/src/MACLib/APECompress.cpp --- mac-3.99-u4-b5/src/MACLib/APECompress.cpp 2006-06-01 05:00:58.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/APECompress.cpp 2008-03-12 11:26:59.000000000 -0400 @@ -132,10 +132,18 @@ return ERROR_SUCCESS; } +#ifdef SHNTOOL +int CAPECompress::Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, const void * pHeaderData, int nHeaderBytes) +#else int CAPECompress::Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) +#endif { RETURN_ON_ERROR(ProcessBuffer(TRUE)) +#ifdef SHNTOOL + return m_spAPECompressCreate->Finish(pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, pHeaderData, nHeaderBytes); +#else return m_spAPECompressCreate->Finish(pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes); +#endif } int CAPECompress::Kill() @@ -224,10 +232,20 @@ // get data int nBlocksAdded = 0; int nRetVal = pInputSource->GetData(pBuffer, nBlocksToAdd, &nBlocksAdded); + +#ifdef SHNTOOL + nBytesRead = (nBlocksAdded * m_wfeInput.nBlockAlign); + if (nRetVal != 0) + { + UnlockBuffer(nBytesRead, TRUE); + return ERROR_IO_READ; + } +#else if (nRetVal != 0) return ERROR_IO_READ; else nBytesRead = (nBlocksAdded * m_wfeInput.nBlockAlign); +#endif // store the bytes read if (pBytesAdded) @@ -243,4 +261,3 @@ return ERROR_SUCCESS; } - diff -urN mac-3.99-u4-b5/src/MACLib/APECompress.h mac-3.99-u4-b5-s4/src/MACLib/APECompress.h --- mac-3.99-u4-b5/src/MACLib/APECompress.h 2006-06-01 05:00:57.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/APECompress.h 2008-03-12 11:26:32.000000000 -0400 @@ -32,7 +32,11 @@ int AddDataFromInputSource(CInputSource * pInputSource, int nMaxBytes = -1, int * pBytesAdded = NULL); // finish / kill +#ifdef SHNTOOL + int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, const void * pHeaderData, int nHeaderBytes); +#else int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes); +#endif int Kill(); private: diff -urN mac-3.99-u4-b5/src/MACLib/APECompressCreate.cpp mac-3.99-u4-b5-s4/src/MACLib/APECompressCreate.cpp --- mac-3.99-u4-b5/src/MACLib/APECompressCreate.cpp 2006-06-01 05:00:57.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/APECompressCreate.cpp 2008-03-12 11:25:54.000000000 -0400 @@ -91,14 +91,23 @@ return nRetVal; } +#ifdef SHNTOOL +int CAPECompressCreate::Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, const void * pHeaderData, int nHeaderBytes) +#else int CAPECompressCreate::Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) +#endif { // clear the bit array RETURN_ON_ERROR(m_spAPECompressCore->GetBitArray()->OutputBitArray(TRUE)); // finalize the file +#ifdef SHNTOOL + RETURN_ON_ERROR(FinalizeFile(m_spIO, m_nFrameIndex, m_nLastFrameBlocks, + pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, m_spAPECompressCore->GetPeakLevel(), pHeaderData, nHeaderBytes)); +#else RETURN_ON_ERROR(FinalizeFile(m_spIO, m_nFrameIndex, m_nLastFrameBlocks, pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, m_spAPECompressCore->GetPeakLevel())); +#endif return ERROR_SUCCESS; } @@ -167,8 +176,22 @@ return ERROR_SUCCESS; } +#ifdef SHNTOOL +void ULONG_TO_UCHAR_LE(unsigned char * buf,unsigned long num) +/* converts an unsigned long to 4 bytes stored in little-endian format */ +{ + buf[0] = (unsigned char)(num); + buf[1] = (unsigned char)(num >> 8); + buf[2] = (unsigned char)(num >> 16); + buf[3] = (unsigned char)(num >> 24); +} +#endif +#ifdef SHNTOOL +int CAPECompressCreate::FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel, const void * pHeaderData, int nHeaderBytes) +#else int CAPECompressCreate::FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel) +#endif { // store the tail position int nTailPosition = pIO->GetPosition(); @@ -203,7 +226,7 @@ // update the header APEHeader.nFinalFrameBlocks = nFinalFrameBlocks; APEHeader.nTotalFrames = nNumberOfFrames; - + // update the descriptor APEDescriptor.nAPEFrameDataBytes = nTailPosition - (APEDescriptor.nDescriptorBytes + APEDescriptor.nHeaderBytes + APEDescriptor.nSeekTableBytes + APEDescriptor.nHeaderDataBytes); APEDescriptor.nAPEFrameDataBytesHigh = 0; @@ -239,6 +262,54 @@ m_spSeekTable[i] = swap_int32(m_spSeekTable[i]); } #endif - + +#ifdef SHNTOOL + char *p; + int i; + uint32 nDataSize; + bool bHasWaveHeader = ((pHeaderData != NULL) && (nHeaderBytes > 0) && (nHeaderBytes != CREATE_WAV_HEADER_ON_DECOMPRESSION)); + + // rewrite the WAVE header with known, correct values + if (bHasWaveHeader) + { + CSmartPtr spOldHeader; + spOldHeader.Assign(new unsigned char [nHeaderBytes], TRUE); + memcpy(spOldHeader,pHeaderData,nHeaderBytes); + + nDataSize = (((nNumberOfFrames - 1) * m_nSamplesPerFrame + nFinalFrameBlocks) * m_wfeInput.nBlockAlign); + for (i = 0; i < nHeaderBytes; i++) + { + if ((p = strstr((const char *)pHeaderData+i,"RIFF"))) { + ULONG_TO_UCHAR_LE((unsigned char *)p+4,nDataSize + nHeaderBytes - 8); + break; + } + } + for (i = 0; i < nHeaderBytes; i++) + { + if ((p = strstr((const char *)pHeaderData+i,"data"))) { + ULONG_TO_UCHAR_LE((unsigned char *)p+4,nDataSize); + break; + } + } + + if (pIO->Write((void *) pHeaderData, nHeaderBytes, &nBytesWritten) != 0) { return ERROR_IO_WRITE; } + + if (memcmp(spOldHeader,pHeaderData,nHeaderBytes)) + { + swap_ape_descriptor(&APEDescriptor); + + GetChecksum(pIO,&APEDescriptor,0,APEDescriptor.cFileMD5); + + swap_ape_descriptor(&APEDescriptor); + + if (pIO->Seek(0, FILE_BEGIN) != ERROR_SUCCESS) { return ERROR_IO_WRITE; } + + if (pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten) != 0) { return ERROR_IO_WRITE; } + } + + spOldHeader.Delete(); + } +#endif + return ERROR_SUCCESS; } diff -urN mac-3.99-u4-b5/src/MACLib/APECompressCreate.h mac-3.99-u4-b5-s4/src/MACLib/APECompressCreate.h --- mac-3.99-u4-b5/src/MACLib/APECompressCreate.h 2006-06-01 05:00:58.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/APECompressCreate.h 2008-03-12 11:24:53.000000000 -0400 @@ -12,7 +12,11 @@ ~CAPECompressCreate(); int InitializeFile(CIO * pIO, const WAVEFORMATEX * pwfeInput, int nMaxFrames, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes); +#ifdef SHNTOOL + int FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel, const void * pHeaderData, int nHeaderBytes); +#else int FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel); +#endif int SetSeekByte(int nFrame, int nByteOffset); @@ -21,7 +25,11 @@ int GetFullFrameBytes(); int EncodeFrame(const void * pInputData, int nInputBytes); +#ifdef SHNTOOL + int Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, const void * pHeaderData, int nHeaderBytes); +#else int Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes); +#endif private: diff -urN mac-3.99-u4-b5/src/MACLib/APESimple.cpp mac-3.99-u4-b5-s4/src/MACLib/APESimple.cpp --- mac-3.99-u4-b5/src/MACLib/APESimple.cpp 2006-06-01 05:00:58.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/APESimple.cpp 2008-03-12 11:28:10.000000000 -0400 @@ -44,18 +44,29 @@ int __stdcall VerifyFile(const str_ansi * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible) { CSmartPtr spInputFile(GetUTF16FromANSI(pInputFilename), TRUE); +#ifdef SHNTOOL + return VerifyFileW(spInputFile, pPercentageDone, ProgressCallback, pKillFlag, bQuickVerifyIfPossible); +#else return VerifyFileW(spInputFile, pPercentageDone, ProgressCallback, pKillFlag, FALSE); +#endif } /***************************************************************************************** Compress file *****************************************************************************************/ +#ifdef SHNTOOL int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag) +#else +int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag) +#endif { // declare the variables int nFunctionRetVal = ERROR_SUCCESS; WAVEFORMATEX WaveFormatEx; CSmartPtr spMACProgressHelper; +#ifdef SHNTOOL + CSmartPtr spHeader; +#endif CSmartPtr spBuffer; CSmartPtr spAPECompress; @@ -78,13 +89,20 @@ int nAudioBytes = nAudioBlocks * WaveFormatEx.nBlockAlign; // start the encoder +#ifdef SHNTOOL + if (nHeaderBytes > 0) spHeader.Assign(new unsigned char [nHeaderBytes], TRUE); + THROW_ON_ERROR(spInputSource->GetHeaderData(spHeader.GetPtr())) + THROW_ON_ERROR(spAPECompress->Start(pOutputFilename, &WaveFormatEx, nAudioBytes, + nCompressionLevel, spHeader.GetPtr(), nHeaderBytes)); +#else if (nHeaderBytes > 0) spBuffer.Assign(new unsigned char [nHeaderBytes], TRUE); THROW_ON_ERROR(spInputSource->GetHeaderData(spBuffer.GetPtr())) THROW_ON_ERROR(spAPECompress->Start(pOutputFilename, &WaveFormatEx, nAudioBytes, nCompressionLevel, spBuffer.GetPtr(), nHeaderBytes)); spBuffer.Delete(); - +#endif + // set-up the progress spMACProgressHelper.Assign(new CMACProgressHelper(nAudioBytes, pPercentageDone, ProgressCallback, pKillFlag)); @@ -94,9 +112,30 @@ while (nBytesLeft > 0) { int nBytesAdded = 0; +#ifdef SHNTOOL + nRetVal = spAPECompress->AddDataFromInputSource(spInputSource.GetPtr(), nBytesLeft, &nBytesAdded); + switch (nRetVal) { + case 0: + if (nBytesAdded == 0) + { + nBytesLeft = 0; + nTerminatingBytes = 0; + } + nBytesLeft -= nBytesAdded; + break; + case ERROR_IO_READ: + nBytesLeft = 0; + nTerminatingBytes = 0; + break; + default: + throw(nRetVal); + break; + } +#else THROW_ON_ERROR(spAPECompress->AddDataFromInputSource(spInputSource.GetPtr(), nBytesLeft, &nBytesAdded)) nBytesLeft -= nBytesAdded; +#endif // update the progress spMACProgressHelper->UpdateProgress(nAudioBytes - nBytesLeft); @@ -109,7 +148,11 @@ // finalize the file if (nTerminatingBytes > 0) spBuffer.Assign(new unsigned char [nTerminatingBytes], TRUE); THROW_ON_ERROR(spInputSource->GetTerminatingData(spBuffer.GetPtr())); +#ifdef SHNTOOL + THROW_ON_ERROR(spAPECompress->Finish(spBuffer.GetPtr(), nTerminatingBytes, nTerminatingBytes, spHeader.GetPtr(), nHeaderBytes)) +#else THROW_ON_ERROR(spAPECompress->Finish(spBuffer.GetPtr(), nTerminatingBytes, nTerminatingBytes)) +#endif // update the progress to 100% spMACProgressHelper->UpdateProgressComplete(); @@ -131,6 +174,42 @@ return nFunctionRetVal; } +int __stdcall GetChecksum(CIO * pIO, APE_DESCRIPTOR * spAPEDescriptor, int nJunkHeaderBytes, unsigned char * cChecksum) +{ + unsigned int nBytesRead = 0; + CMD5Helper MD5Helper; + + int nHead = nJunkHeaderBytes + spAPEDescriptor->nDescriptorBytes; + int nStart = nHead + spAPEDescriptor->nHeaderBytes + spAPEDescriptor->nSeekTableBytes; + + pIO->Seek(nHead, FILE_BEGIN); + int nHeadBytes = nStart - nHead; + CSmartPtr spHeadBuffer(new unsigned char [nHeadBytes], TRUE); + if ((pIO->Read(spHeadBuffer, nHeadBytes, &nBytesRead) != ERROR_SUCCESS) || (nHeadBytes != int(nBytesRead))) + return ERROR_IO_READ; + + int nBytesLeft = spAPEDescriptor->nHeaderDataBytes + spAPEDescriptor->nAPEFrameDataBytes + spAPEDescriptor->nTerminatingDataBytes; + CSmartPtr spBuffer(new unsigned char [16384], TRUE); + nBytesRead = 1; + while ((nBytesLeft > 0) && (nBytesRead > 0)) + { + int nBytesToRead = min(16384, nBytesLeft); + if (pIO->Read(spBuffer, nBytesToRead, &nBytesRead) != ERROR_SUCCESS) + return ERROR_IO_READ; + + MD5Helper.AddData(spBuffer, nBytesRead); + nBytesLeft -= nBytesRead; + } + + if (nBytesLeft != 0) + return ERROR_IO_READ; + + MD5Helper.AddData(spHeadBuffer, nHeadBytes); + + MD5Helper.GetResult(cChecksum); + + return ERROR_SUCCESS; +} /***************************************************************************************** Verify file @@ -173,53 +252,25 @@ { // variable declares int nFunctionRetVal = ERROR_SUCCESS; - unsigned int nBytesRead = 0; CSmartPtr spAPEDecompress; // run the quick verify try { + unsigned char cResult[16]; + spAPEDecompress.Assign(CreateIAPEDecompress(pInputFilename, &nFunctionRetVal)); if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal); - CMD5Helper MD5Helper; - - CIO * pIO = GET_IO(spAPEDecompress); APE_FILE_INFO * pInfo = (APE_FILE_INFO *) spAPEDecompress->GetInfo(APE_INTERNAL_INFO); - if ((pInfo->nVersion < 3980) || (pInfo->spAPEDescriptor == NULL)) - throw(ERROR_UPSUPPORTED_FILE_VERSION); - - int nHead = pInfo->nJunkHeaderBytes + pInfo->spAPEDescriptor->nDescriptorBytes; - int nStart = nHead + pInfo->spAPEDescriptor->nHeaderBytes + pInfo->spAPEDescriptor->nSeekTableBytes; - - pIO->Seek(nHead, FILE_BEGIN); - int nHeadBytes = nStart - nHead; - CSmartPtr spHeadBuffer(new unsigned char [nHeadBytes], TRUE); - if ((pIO->Read(spHeadBuffer, nHeadBytes, &nBytesRead) != ERROR_SUCCESS) || (nHeadBytes != int(nBytesRead))) - throw(ERROR_IO_READ); - - int nBytesLeft = pInfo->spAPEDescriptor->nHeaderDataBytes + pInfo->spAPEDescriptor->nAPEFrameDataBytes + pInfo->spAPEDescriptor->nTerminatingDataBytes; - CSmartPtr spBuffer(new unsigned char [16384], TRUE); - nBytesRead = 1; - while ((nBytesLeft > 0) && (nBytesRead > 0)) - { - int nBytesToRead = min(16384, nBytesLeft); - if (pIO->Read(spBuffer, nBytesToRead, &nBytesRead) != ERROR_SUCCESS) - throw(ERROR_IO_READ); + return ERROR_UPSUPPORTED_FILE_VERSION; - MD5Helper.AddData(spBuffer, nBytesRead); - nBytesLeft -= nBytesRead; - } + CIO * pIO = GET_IO(spAPEDecompress); - if (nBytesLeft != 0) + if (GetChecksum(pIO,pInfo->spAPEDescriptor,pInfo->nJunkHeaderBytes,cResult) != ERROR_SUCCESS) throw(ERROR_IO_READ); - MD5Helper.AddData(spHeadBuffer, nHeadBytes); - - unsigned char cResult[16]; - MD5Helper.GetResult(cResult); - if (memcmp(cResult, pInfo->spAPEDescriptor->cFileMD5, 16) != 0) nFunctionRetVal = ERROR_INVALID_CHECKSUM; @@ -401,11 +452,19 @@ THROW_ON_ERROR(GET_IO(spAPEDecompress)->Read(&spTempBuffer[spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)], nTagBytes, &nBytesRead)) } +#ifdef SHNTOOL + THROW_ON_ERROR(spAPECompress->Finish(spTempBuffer, nTerminatingBytes, spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES), 0, 0)); +#else THROW_ON_ERROR(spAPECompress->Finish(spTempBuffer, nTerminatingBytes, spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES))); +#endif } else { +#ifdef SHNTOOL + THROW_ON_ERROR(spAPECompress->Finish(NULL, 0, 0, 0, 0)); +#else THROW_ON_ERROR(spAPECompress->Finish(NULL, 0, 0)); +#endif } } diff -urN mac-3.99-u4-b5/src/MACLib/MACLib.h mac-3.99-u4-b5-s4/src/MACLib/MACLib.h --- mac-3.99-u4-b5/src/MACLib/MACLib.h 2006-06-01 05:00:58.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/MACLib.h 2008-03-12 11:24:18.000000000 -0400 @@ -394,7 +394,11 @@ // the number of bytes of the terminating data buffer that should be appended to a decoded // WAV file (it's basically nTerminatingBytes - the bytes that make up the tag) ////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef SHNTOOL + virtual int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, const void * pHeaderData, int nHeaderBytes) = 0; +#else virtual int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) = 0; +#endif ////////////////////////////////////////////////////////////////////////////////////////////// // Kill(...) - stops encoding and deletes the output file @@ -436,11 +440,20 @@ DLLEXPORT int __stdcall DecompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); DLLEXPORT int __stdcall ConvertFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); DLLEXPORT int __stdcall VerifyFile(const str_ansi * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); + DLLEXPORT int __stdcall GetChecksum(CIO * pIO, APE_DESCRIPTOR * spAPEDescriptor, int nJunkHeaderBytes, unsigned char * cChecksum); +#ifdef SHNTOOL + DLLEXPORT int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL); +#else DLLEXPORT int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL); +#endif DLLEXPORT int __stdcall DecompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); DLLEXPORT int __stdcall ConvertFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); - DLLEXPORT int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible = FALSE); +#ifdef SHNTOOL + DLLEXPORT int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible = TRUE); +#else + DLLEXPORT int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible = FALSE); +#endif // helper functions DLLEXPORT int __stdcall FillWaveFormatEx(WAVEFORMATEX * pWaveFormatEx, int nSampleRate = 44100, int nBitsPerSample = 16, int nChannels = 2); diff -urN mac-3.99-u4-b5/src/MACLib/WAVInputSource.cpp mac-3.99-u4-b5-s4/src/MACLib/WAVInputSource.cpp --- mac-3.99-u4-b5/src/MACLib/WAVInputSource.cpp 2006-06-01 05:00:57.000000000 -0400 +++ mac-3.99-u4-b5-s4/src/MACLib/WAVInputSource.cpp 2008-03-12 02:13:26.000000000 -0400 @@ -31,9 +31,25 @@ uint32 nChunkBytes; // the bytes of the chunk }; +#ifdef SHNTOOL +unsigned long UCHAR_TO_ULONG_LE(unsigned char * buf) +/* converts 4 bytes stored in little-endian format to an unsigned long */ +{ + return (unsigned long)((buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0]); +} + +unsigned short UCHAR_TO_USHORT_LE(unsigned char * buf) +/* converts 2 bytes stored in little-endian format to an unsigned short */ +{ + return (unsigned short)((buf[1] << 8) + buf[0]); +} +#endif CInputSource * CreateInputSource(const wchar_t * pSourceName, WAVEFORMATEX * pwfeSource, int * pTotalBlocks, int * pHeaderBytes, int * pTerminatingBytes, int * pErrorCode) { +#ifdef SHNTOOL + return new CWAVInputSource(pSourceName, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes, pErrorCode); +#else // error check the parameters if ((pSourceName == NULL) || (wcslen(pSourceName) == 0)) { @@ -57,6 +73,7 @@ if (pErrorCode) *pErrorCode = ERROR_INVALID_INPUT_FILE; return NULL; } +#endif } CWAVInputSource::CWAVInputSource(CIO * pIO, WAVEFORMATEX * pwfeSource, int * pTotalBlocks, int * pHeaderBytes, int * pTerminatingBytes, int * pErrorCode) @@ -139,6 +156,101 @@ int CWAVInputSource::AnalyzeSource() { +#ifdef SHNTOOL + unsigned char *p = m_sFullHeader, *priff = NULL; + + // seek to the beginning (just in case) + m_spIO->Seek(0, FILE_BEGIN); + + // get the file size + m_nFileBytes = m_spIO->GetSize(); + + // get the RIFF header + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(RIFF_HEADER))) + + // make sure the RIFF header is valid + if (!(p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F')) + return ERROR_INVALID_INPUT_FILE; + p += sizeof(RIFF_HEADER); + + // read the data type header + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(DATA_TYPE_ID_HEADER))) + + // make sure it's the right data type + if (!(p[0] == 'W' && p[1] == 'A' && p[2] == 'V' && p[3] == 'E')) + return ERROR_INVALID_INPUT_FILE; + p += sizeof(DATA_TYPE_ID_HEADER); + + // find the 'fmt ' chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(RIFF_CHUNK_HEADER))) + + while (!(p[0] == 'f' && p[1] == 'm' && p[2] == 't' && p[3] == ' ')) + { + p += sizeof(RIFF_CHUNK_HEADER); + + // move the file pointer to the end of this chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, UCHAR_TO_ULONG_LE(p+4))) + p += UCHAR_TO_ULONG_LE(p+4); + + // check again for the data chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(RIFF_CHUNK_HEADER))) + } + + priff = p+4; + p += sizeof(RIFF_CHUNK_HEADER); + + // read the format info + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(WAV_FORMAT_HEADER))) + + // error check the header to see if we support it + if (UCHAR_TO_USHORT_LE(p) != 1) + return ERROR_INVALID_INPUT_FILE; + + // copy the format information to the WAVEFORMATEX passed in + FillWaveFormatEx(&m_wfeSource, UCHAR_TO_ULONG_LE(p+4), UCHAR_TO_USHORT_LE(p+14), UCHAR_TO_USHORT_LE(p+2)); + + p += sizeof(WAV_FORMAT_HEADER); + + // skip over any extra data in the header + int nWAVFormatHeaderExtra = UCHAR_TO_ULONG_LE(priff) - sizeof(WAV_FORMAT_HEADER); + if (nWAVFormatHeaderExtra < 0) + return ERROR_INVALID_INPUT_FILE; + else { + RETURN_ON_ERROR(ReadSafe(m_spIO, p, nWAVFormatHeaderExtra)) + p += nWAVFormatHeaderExtra; + } + + // find the data chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(RIFF_CHUNK_HEADER))) + + while (!(p[0] == 'd' && p[1] == 'a' && p[2] == 't' && p[3] == 'a')) + { + p += sizeof(RIFF_CHUNK_HEADER); + + // move the file pointer to the end of this chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, UCHAR_TO_ULONG_LE(p+4))) + p += UCHAR_TO_ULONG_LE(p+4); + + // check again for the data chunk + RETURN_ON_ERROR(ReadSafe(m_spIO, p, sizeof(RIFF_CHUNK_HEADER))) + } + + // we're at the data block + m_nDataBytes = UCHAR_TO_ULONG_LE(p+4); + if (m_nDataBytes < 0) + m_nDataBytes = m_nFileBytes - m_nHeaderBytes; + + p += sizeof(RIFF_CHUNK_HEADER); + + m_nHeaderBytes = p - m_sFullHeader; + + // make sure the data bytes is a whole number of blocks + if ((m_nDataBytes % m_wfeSource.nBlockAlign) != 0) + return ERROR_INVALID_INPUT_FILE; + + // calculate the terminating byts + m_nTerminatingBytes = 0; +#else // seek to the beginning (just in case) m_spIO->Seek(0, FILE_BEGIN); @@ -222,9 +334,10 @@ // calculate the terminating byts m_nTerminatingBytes = m_nFileBytes - m_nDataBytes - m_nHeaderBytes; - - // we made it this far, everything must be cool - return ERROR_SUCCESS; +#endif + + // we made it this far, everything must be cool + return ERROR_SUCCESS; } int CWAVInputSource::GetData(unsigned char * pBuffer, int nBlocks, int * pBlocksRetrieved) @@ -234,16 +347,35 @@ int nBytes = (m_wfeSource.nBlockAlign * nBlocks); unsigned int nBytesRead = 0; +#ifdef SHNTOOL + int nRetVal = m_spIO->Read(pBuffer, nBytes, &nBytesRead); +#else if (m_spIO->Read(pBuffer, nBytes, &nBytesRead) != ERROR_SUCCESS) return ERROR_IO_READ; +#endif if (pBlocksRetrieved) *pBlocksRetrieved = (nBytesRead / m_wfeSource.nBlockAlign); +#ifdef SHNTOOL + if (nRetVal != ERROR_SUCCESS) + return ERROR_IO_READ; +#endif + return ERROR_SUCCESS; } int CWAVInputSource::GetHeaderData(unsigned char * pBuffer) { +#ifdef SHNTOOL + int i; + + if (!m_bIsValid) return ERROR_UNDEFINED; + + for (i=0;i spName(GetANSIFromUTF16(pName), TRUE); #endif - m_hFile = ::CreateFile(spName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (m_hFile == INVALID_HANDLE_VALUE) - { - m_hFile = ::CreateFile(spName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (m_hFile == INVALID_HANDLE_VALUE) - { - return -1; - } - else - { - m_bReadOnly = TRUE; - } - } - else - { - m_bReadOnly = FALSE; +#ifdef SHNTOOL + if ( 0 == wcscmp (pName, L"-") ) + { + m_hFile = GetStdHandle (STD_INPUT_HANDLE); + m_bReadOnly = TRUE; + // ReadOnly + } + else + { +#endif + m_hFile = ::CreateFile(spName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_hFile == INVALID_HANDLE_VALUE) + { + m_hFile = ::CreateFile(spName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_hFile == INVALID_HANDLE_VALUE) + { + return -1; + } + else + { + m_bReadOnly = TRUE; + } + } + else + { + m_bReadOnly = FALSE; + } +#ifdef SHNTOOL } +#endif wcscpy(m_cFileName, pName); @@ -136,10 +149,23 @@ CSmartPtr spName(GetANSIFromUTF16(pName), TRUE); #endif - m_hFile = CreateFile(spName, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (m_hFile == INVALID_HANDLE_VALUE) { return -1; } +#ifdef SHNTOOL + if ( 0 == wcscmp (pName, L"-") ) + { + m_hFile = GetStdHandle (STD_OUTPUT_HANDLE); + m_bReadOnly = FALSE; + // ReadOnly + } + else + { +#endif + m_hFile = CreateFile(spName, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_hFile == INVALID_HANDLE_VALUE) { return -1; } - m_bReadOnly = FALSE; + m_bReadOnly = FALSE; +#ifdef SHNTOOL + } +#endif wcscpy(m_cFileName, pName);