From 11f1d89cee7d09b8ffa2350cd2ee5a1b929260bf Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Mon, 24 Feb 2014 10:49:04 +0100 Subject: + If BitsPerSample in a WAVE file is less than 16, StormLib does not use ADPCM compression + Added unit test for MONO and MONO-less-than-16-BitsPerSample WAVE files --- src/SFileAddFile.cpp | 64 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'src/SFileAddFile.cpp') 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; } -- cgit v1.2.3