+ 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
This commit is contained in:
Ladislav Zezula
2014-02-24 10:49:04 +01:00
parent a49ccff3d1
commit 11f1d89cee
2 changed files with 55 additions and 35 deletions

View File

@@ -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;
}