diff options
-rw-r--r-- | src/SFileAddFile.cpp | 64 | ||||
-rw-r--r-- | test/Test.cpp | 26 |
2 files changed, 55 insertions, 35 deletions
diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index 1fa5873..a7d99ea 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -14,7 +14,17 @@ #include "StormCommon.h" //----------------------------------------------------------------------------- -// Local structures +// Local variables + +// Mask for lossy compressions +#define MPQ_LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) + +// Data compression for SFileAddFile +// Kept here for compatibility with code that was created with StormLib version < 6.50 +static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; + +//----------------------------------------------------------------------------- +// WAVE verification #define FILE_SIGNATURE_RIFF 0x46464952 #define FILE_SIGNATURE_WAVE 0x45564157 @@ -40,34 +50,29 @@ typedef struct _WAVE_FILE_HEADER // Followed by "data" sub-chunk (we don't care) } WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; -//----------------------------------------------------------------------------- -// Local variables - -// Data compression for SFileAddFile -// Kept here for compatibility with code that was created with StormLib version < 6.50 -static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; - - -//----------------------------------------------------------------------------- -// MPQ write data functions - -#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) - -static bool IsWaveFile( +static bool IsWaveFile_16BitsPerAdpcmSample( LPBYTE pbFileData, DWORD cbFileData, LPDWORD pdwChannels) { PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData; + // The amount of file data must be at least size of WAVE header if(cbFileData > sizeof(WAVE_FILE_HEADER)) { + // Check for the RIFF header if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE) { + // Check for ADPCM format if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM) { - *pdwChannels = pWaveHdr->wChannels; - return true; + // Now the number of bits per sample must be at least 16. + // If not, the WAVE file gets corrupted by the ADPCM compression + if(pWaveHdr->wBitsPerSample >= 0x10) + { + *pdwChannels = pWaveHdr->wChannels; + return true; + } } } } @@ -75,6 +80,8 @@ static bool IsWaveFile( return false; } +//----------------------------------------------------------------------------- +// MPQ write data functions static int WriteDataToMpqFile( TMPQArchive * ha, @@ -178,7 +185,7 @@ static int WriteDataToMpqFile( // If the caller wants ADPCM compression, we will set wave compression level to 4, // which corresponds to medium quality - nCompressionLevel = (dwCompression & LOSSY_COMPRESSION_MASK) ? 4 : -1; + nCompressionLevel = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? 4 : -1; SCompCompress(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer, (unsigned)dwCompression, 0, nCompressionLevel); } @@ -751,7 +758,7 @@ bool WINAPI SFileWriteFile( // nError = ERROR_INVALID_PARAMETER; // Lossy compression is not allowed on single unit files - if(dwCompression & LOSSY_COMPRESSION_MASK) + if(dwCompression & MPQ_LOSSY_COMPRESSION_MASK) nError = ERROR_INVALID_PARAMETER; } @@ -855,7 +862,8 @@ bool WINAPI SFileAddFileEx( // that the first sector is not compressed with lossy compression if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) { - // The first compression must not be WAVE + // The compression of the first file sector must not be ADPCM + // in order not to corrupt the headers if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; @@ -888,15 +896,19 @@ bool WINAPI SFileAddFileEx( // If the file being added is a WAVE file, we check number of channels if(bIsFirstSector && bIsAdpcmCompression) { - // The file must really be a wave file, otherwise it will corrupt the file - if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels)) + // The file must really be a WAVE file with at least 16 bits per sample, + // otherwise the ADPCM compression will corrupt it + if(IsWaveFile_16BitsPerAdpcmSample(pbFileData, dwBytesToRead, &dwChannels)) { - nError = ERROR_BAD_FORMAT; - break; + // Setup the compression of next sectors according to number of channels + dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; + } + else + { + // Setup the compression of next sectors to a lossless compression + dwCompressionNext = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? MPQ_COMPRESSION_PKWARE : dwCompression; } - // Setup the compression according to number of channels - dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; bIsFirstSector = false; } diff --git a/test/Test.cpp b/test/Test.cpp index 70e06d1..940a013 100644 --- a/test/Test.cpp +++ b/test/Test.cpp @@ -87,7 +87,7 @@ static DWORD AddFlags[] = 0xFFFFFFFF }; -static DWORD Compressions[] = +static DWORD WaveCompressions[] = { MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN, MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN, @@ -3176,22 +3176,22 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName) return nError; } -static int TestCreateArchive_CompressionsTest(const char * szPlainName) +static int TestCreateArchive_WaveCompressionsTest(const char * szPlainName, const char * szWaveFile) { TLogHelper Logger("CompressionsTest", szPlainName); HANDLE hMpq = NULL; // Handle of created archive char szFileName[MAX_PATH]; // Source file to be added char szArchivedName[MAX_PATH]; - DWORD dwCmprCount = sizeof(Compressions) / sizeof(DWORD); + DWORD dwCmprCount = sizeof(WaveCompressions) / sizeof(DWORD); DWORD dwAddedFiles = 0; DWORD dwFoundFiles = 0; int nError; // Create paths for local file to be added - CreateFullPathName(szFileName, szMpqSubDir, "AddFile.wav"); + CreateFullPathName(szFileName, szMpqSubDir, szWaveFile); // Create new archive - nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4, 0x40, &hMpq); + nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1, 0x40, &hMpq); // Add the same file multiple times if(nError == ERROR_SUCCESS) @@ -3200,7 +3200,7 @@ static int TestCreateArchive_CompressionsTest(const char * szPlainName) for(unsigned int i = 0; i < dwCmprCount; i++) { sprintf(szArchivedName, "WaveFile_%02u.wav", i + 1); - nError = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_SECTOR_CRC, Compressions[i]); + nError = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_SECTOR_CRC, WaveCompressions[i]); if(nError != ERROR_SUCCESS) break; @@ -3694,10 +3694,18 @@ int main(int argc, char * argv[]) if(nError == ERROR_SUCCESS) nError = TestCreateArchive_FileFlagTest("StormLibTest_FileFlagTest.mpq"); - // Create a MPQ file, add files with various compressions + // Create a MPQ file, add a mono-WAVE file with various compressions if(nError == ERROR_SUCCESS) - nError = TestCreateArchive_CompressionsTest("StormLibTest_CompressionTest.mpq"); + nError = TestCreateArchive_WaveCompressionsTest("StormLibTest_AddWaveMonoTest.mpq", "AddFile-Mono.wav"); + // Create a MPQ file, add a mono-WAVE with 8 bits per sample file with various compressions + if(nError == ERROR_SUCCESS) + nError = TestCreateArchive_WaveCompressionsTest("StormLibTest_AddWaveMonoBadTest.mpq", "AddFile-MonoBad.wav"); + + // Create a MPQ file, add a stereo-WAVE file with various compressions + if(nError == ERROR_SUCCESS) + nError = TestCreateArchive_WaveCompressionsTest("StormLibTest_AddWaveStereoTest.mpq", "AddFile-Stereo.wav"); +/* // Check if the listfile is always created at the end of the file table in the archive if(nError == ERROR_SUCCESS) nError = TestCreateArchive_ListFilePos("StormLibTest_ListFilePos.mpq"); @@ -3705,6 +3713,6 @@ int main(int argc, char * argv[]) // Open a MPQ (add custom user data to it) if(nError == ERROR_SUCCESS) nError = TestCreateArchive_BigArchive("StormLibTest_BigArchive_v4.mpq"); - +*/ return nError; } |