View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0000914 | OpenMPT | libopenmpt | public | 2017-01-24 10:39 | 2017-09-21 06:44 |
Reporter | manx | Assigned To | manx | ||
Priority | high | Severity | major | Reproducibility | always |
Status | resolved | Resolution | fixed | ||
Product Version | OpenMPT 1.27.00.* (old testing) | ||||
Target Version | OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first) | Fixed in Version | OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first) | ||
Summary | 0000914: split module format probing into separate TestFOO() static member functions | ||||
Description | Split module format header probing into separate TestFOO() static member functions to allow for probing without creating a CSoundFile instance (which is rather slow) | ||||
Tags | No tags attached. | ||||
Attached Files | probe-refactor-v11.patch (107,955 bytes)
Index: soundlib/ContainerMMCMP.cpp =================================================================== --- soundlib/ContainerMMCMP.cpp (revision 8886) +++ soundlib/ContainerMMCMP.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -144,6 +145,66 @@ } +static bool ValidateHeader(const MMCMPFILEHEADER &mfh) +//---------------------------------------------------- +{ + if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) + { + return false; + } + if(mfh.hdrsize != sizeof(MMCMPHEADER)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const MMCMPHEADER &mmh) +//------------------------------------------------ +{ + if(mmh.nblocks == 0) + { + return false; + } + if(mmh.filesize == 0) + { + return false; + } + if(mmh.filesize > 0x80000000) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + MMCMPFILEHEADER mfh; + if(!file.ReadStruct(mfh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mfh)) + { + return ProbeFailure; + } + MMCMPHEADER mmh; + if(!file.ReadStruct(mmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mmh)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------- { @@ -151,14 +212,23 @@ containerItems.clear(); MMCMPFILEHEADER mfh; - if(!file.ReadStruct(mfh)) return false; - if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) return false; - if(mfh.hdrsize != sizeof(MMCMPHEADER)) return false; + if(!file.ReadStruct(mfh)) + { + return false; + } + if(!ValidateHeader(mfh)) + { + return false; + } MMCMPHEADER mmh; - if(!file.ReadStruct(mmh)) return false; - if(mmh.nblocks == 0) return false; - if(mmh.filesize == 0) return false; - if(mmh.filesize > 0x80000000) return false; + if(!file.ReadStruct(mmh)) + { + return false; + } + if(!ValidateHeader(mmh)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; Index: soundlib/ContainerPP20.cpp =================================================================== --- soundlib/ContainerPP20.cpp (revision 8886) +++ soundlib/ContainerPP20.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -115,6 +116,51 @@ } +struct PP20header +{ + char magic[4]; // "PP20" + uint8be dummy[8]; // unknown + uint8be efficiency[4]; +}; + +MPT_BINARY_STRUCT(PP20header, 16) + + +static bool ValidateHeader(const PP20header &hdr) +//----------------------------------------------- +{ + if(std::memcmp(hdr.magic, "PP20", 4) != 0) + { + return false; + } + if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15 + || hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15 + || hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15 + || hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PP20header hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackPP20(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------ { @@ -121,15 +167,15 @@ file.Rewind(); containerItems.clear(); - if(!file.ReadMagic("PP20")) return false; - if(!file.CanRead(8)) return false; - uint8 efficiency[4]; - file.ReadArray(efficiency); - if(efficiency[0] < 9 || efficiency[0] > 15 - || efficiency[1] < 9 || efficiency[1] > 15 - || efficiency[2] < 9 || efficiency[2] > 15 - || efficiency[3] < 9 || efficiency[3] > 15) + PP20header hdr; + if(!file.ReadStruct(hdr)) + { return false; + } + if(!ValidateHeader(hdr)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; Index: soundlib/ContainerUMX.cpp =================================================================== --- soundlib/ContainerUMX.cpp (revision 8886) +++ soundlib/ContainerUMX.cpp (working copy) @@ -12,11 +12,48 @@ #include "Loaders.h" #include "UMXTools.h" #include "Container.h" +#include "Sndfile.h" OPENMPT_NAMESPACE_BEGIN +static bool ValidateHeader(const UMXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) + || fileHeader.nameCount == 0 + || fileHeader.exportCount == 0 + || fileHeader.importCount == 0 + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UMXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(!FindUMXNameTableEntryMemory(file, fileHeader, "music")) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -24,15 +61,14 @@ containerItems.clear(); UMXFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) - || fileHeader.nameCount == 0 - || fileHeader.exportCount == 0 - || fileHeader.importCount == 0 - ) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } // Note that this can be a false positive, e.g. Unreal maps will have music and sound // in their name table because they usually import such files. However, it spares us Index: soundlib/ContainerXPK.cpp =================================================================== --- soundlib/ContainerXPK.cpp (revision 8886) +++ soundlib/ContainerXPK.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -329,6 +330,68 @@ } +static bool ValidateHeader(const XPKFILEHEADER &header) +//----------------------------------------------------- +{ + if(std::memcmp(header.XPKF, "XPKF", 4) != 0) + { + return false; + } + if(std::memcmp(header.SQSH, "SQSH", 4) != 0) + { + return false; + } + if(header.SrcLen == 0) + { + return false; + } + if(header.DstLen == 0) + { + return false; + } + STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); + if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize) +//------------------------------------------------------------------------------ +{ + if(filesize != header.SrcLen - 8) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + XPKFILEHEADER header; + if(!file.ReadStruct(header)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(header)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(header, *pfilesize)) + { + return ProbeFailure; + } + } + return ProbeSuccess; +} + + bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -336,19 +399,24 @@ containerItems.clear(); XPKFILEHEADER header; - if(!file.ReadStruct(header)) return false; - if(std::memcmp(header.XPKF, "XPKF", 4) != 0) return false; - if(std::memcmp(header.SQSH, "SQSH", 4) != 0) return false; - if(header.SrcLen == 0) return false; - if(header.DstLen == 0) return false; - STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); - if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) return false; + if(!file.ReadStruct(header)) + { + return false; + } + if(!ValidateHeader(header)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } - if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) return false; + if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) + { + return false; + } + containerItems.emplace_back(); containerItems.back().data_cache = mpt::make_unique<std::vector<char> >(); std::vector<char> & unpackedData = *(containerItems.back().data_cache); Index: soundlib/Load_669.cpp =================================================================== --- soundlib/Load_669.cpp (revision 8886) +++ soundlib/Load_669.cpp (working copy) @@ -62,14 +62,9 @@ MPT_BINARY_STRUCT(_669Sample, 25) -bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const _669FileHeader &fileHeader) { - _669FileHeader fileHeader; - - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.magic, "if", 2) && memcmp(fileHeader.magic, "JN", 2)) + if((std::memcmp(fileHeader.magic, "if", 2) && std::memcmp(fileHeader.magic, "JN", 2)) || fileHeader.samples > 64 || fileHeader.restartPos >= 128 || fileHeader.patterns > 128) @@ -76,21 +71,68 @@ { return false; } - - for(size_t i = 0; i < CountOf(fileHeader.breaks); i++) + for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++) { if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE) + { return false; + } if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0) + { return false; + } if(fileHeader.breaks[i] >= 64) + { return false; + } } + return true; +} + +static uint64 GetHeaderMinimumAdditionalSize(const _669FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + + file.Rewind(); + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) { return true; - } else if(!file.CanRead(fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u)) + } + + if(!file.CanRead(GetHeaderMinimumAdditionalSize(fileHeader))) { return false; } Index: soundlib/Load_amf.cpp =================================================================== --- soundlib/Load_amf.cpp (revision 8886) +++ soundlib/Load_amf.cpp (working copy) @@ -80,6 +80,42 @@ MPT_BINARY_STRUCT(AMFFileHeader, 41) +static bool ValidateHeader(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------ +{ + if(std::memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) + || fileHeader.numSamples > 64 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------------------------ +{ + return 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------------- +{ + AsylumFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------------- { @@ -86,14 +122,16 @@ file.Rewind(); AsylumFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) - || fileHeader.numSamples > 64 - || !file.CanRead(256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -341,6 +379,37 @@ } +static bool ValidateHeader(const AMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.amf, "AMF", 3) + || fileHeader.version < 8 || fileHeader.version > 14 + || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------------- +{ + AMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------ { @@ -347,14 +416,16 @@ file.Rewind(); AMFFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.amf, "AMF", 3) - || fileHeader.version < 8 || fileHeader.version > 14 - || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ams.cpp =================================================================== --- soundlib/Load_ams.cpp (revision 8886) +++ soundlib/Load_ams.cpp (working copy) @@ -332,21 +332,76 @@ MPT_BINARY_STRUCT(AMSSampleHeader, 17) +static bool ValidateHeader(const AMSFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.versionHigh != 0x01) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("Extreme")) + { + return ProbeFailure; + } + AMSFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + if(!file.ReadMagic("Extreme")) + { + return false; + } AMSFileHeader fileHeader; - if(!file.ReadMagic("Extreme") - || !file.ReadStruct(fileHeader) - || !file.Skip(fileHeader.extraSize) - || !file.CanRead(3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u) - || fileHeader.versionHigh != 0x01) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(!file.Skip(fileHeader.extraSize)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -641,30 +696,90 @@ MPT_BINARY_STRUCT(AMS2Description, 11) +static bool ValidateHeader(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------- +{ + if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("AMShdr\x1A")) + { + return ProbeFailure; + } + if(!file.CanRead(1)) + { + return ProbeWantMoreData; + } + const uint8 songNameLength = file.ReadUint8(); + if(!file.Skip(songNameLength)) + { + return ProbeWantMoreData; + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { file.Rewind(); - AMS2FileHeader fileHeader; if(!file.ReadMagic("AMShdr\x1A")) { return false; } - - InitializeGlobals(MOD_TYPE_AMS2); - - if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName) - || !file.ReadStruct(fileHeader) - || fileHeader.versionHigh != 2 || fileHeader.versionLow > 2 - || !file.CanRead(36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u)) + if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + InitializeGlobals(MOD_TYPE_AMS2); + m_nInstruments = fileHeader.numIns; m_nChannels = 32; SetupMODPanning(true); Index: soundlib/Load_dbm.cpp =================================================================== --- soundlib/Load_dbm.cpp (revision 8886) +++ soundlib/Load_dbm.cpp (working copy) @@ -293,19 +293,51 @@ } +static bool ValidateHeader(const DBMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.dbm0, "DBM0", 4) + || fileHeader.trkVerHi > 3) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { - DBMFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.dbm0, "DBM0", 4) - || fileHeader.trkVerHi > 3) + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_digi.cpp =================================================================== --- soundlib/Load_digi.cpp (revision 8886) +++ soundlib/Load_digi.cpp (working copy) @@ -73,6 +73,36 @@ } +static bool ValidateHeader(const DIGIFileHeader &fileHeader) +{ + if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20) + || !fileHeader.numChannels + || fileHeader.numChannels > 8 + || fileHeader.lastOrdIndex > 127) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + DIGIFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { @@ -79,15 +109,16 @@ file.Rewind(); DIGIFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DIGI Booster module\0", 20) - || !fileHeader.numChannels - || fileHeader.numChannels > 8 - || fileHeader.lastOrdIndex > 127) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dmf.cpp =================================================================== --- soundlib/Load_dmf.cpp (revision 8886) +++ soundlib/Load_dmf.cpp (working copy) @@ -887,18 +887,51 @@ } +static bool ValidateHeader(const DMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "DDMF", 4) + || !fileHeader.version || fileHeader.version > 10) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { + file.Rewind(); + DMFFileHeader fileHeader; - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DDMF", 4) - || !fileHeader.version || fileHeader.version > 10) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dsm.cpp =================================================================== --- soundlib/Load_dsm.cpp (revision 8886) +++ soundlib/Load_dsm.cpp (working copy) @@ -100,40 +100,100 @@ MPT_BINARY_STRUCT(DSMSampleHeader, 64) -bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct DSMHeader { - file.Rewind(); + char fileMagic0[4]; + char fileMagic1[4]; + char fileMagic2[4]; +}; - char fileMagic0[4], fileMagic1[4], fileMagic2[4]; - if(!file.ReadArray(fileMagic0)) return false; - if(!file.ReadArray(fileMagic1)) return false; - if(!file.ReadArray(fileMagic2)) return false; +MPT_BINARY_STRUCT(DSMHeader, 12) - if(!memcmp(fileMagic0, "RIFF", 4) - && !memcmp(fileMagic2, "DSMF", 4)) + +static bool ValidateHeader(const DSMHeader &fileHeader) +//----------------------------------------------------- +{ + if(!std::memcmp(fileHeader.fileMagic0, "RIFF", 4) + && !std::memcmp(fileHeader.fileMagic2, "DSMF", 4)) { // "Normal" DSM files with RIFF header // <RIFF> <file size> <DSMF> - } else if(!memcmp(fileMagic0, "DSMF", 4)) + return true; + } else if(!std::memcmp(fileHeader.fileMagic0, "DSMF", 4)) { // DSM files with alternative header // <DSMF> <4 bytes, usually 4x NUL or RIFF> <file size> <4 bytes, usually DSMF but not always> - file.Skip(4); + return true; } else { return false; } +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + if(!file.Skip(4)) + { + return ProbeWantMoreData; + } + } DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(std::memcmp(chunkHeader.magic, "SONG", 4)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} - file.ReadStruct(chunkHeader); + +bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + file.Skip(4); + } + DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return false; + } // Technically, the song chunk could be anywhere in the file, but we're going to simplify // things by not using a chunk header here and just expect it to be right at the beginning. - if(memcmp(chunkHeader.magic, "SONG", 4)) + if(std::memcmp(chunkHeader.magic, "SONG", 4)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_dtm.cpp =================================================================== --- soundlib/Load_dtm.cpp (revision 8886) +++ soundlib/Load_dtm.cpp (working copy) @@ -181,6 +181,37 @@ MPT_BINARY_STRUCT(DTMText, 12) +static bool ValidateHeader(const DTMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "D.T.", 4) + || fileHeader.headerSize < sizeof(fileHeader) - 8u + || fileHeader.headerSize > 256 // Excessively long song title? + || fileHeader.type != 0) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -187,15 +218,16 @@ file.Rewind(); DTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "D.T.", 4) - || fileHeader.headerSize < sizeof(fileHeader) - 8u - || fileHeader.headerSize > 256 // Excessively long song title? - || fileHeader.type != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_far.cpp =================================================================== --- soundlib/Load_far.cpp (revision 8886) +++ soundlib/Load_far.cpp (working copy) @@ -102,6 +102,46 @@ MPT_BINARY_STRUCT(FARSampleHeader, 48) +static bool ValidateHeader(const FARFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 + || std::memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) + ) + { + return false; + } + if(fileHeader.headerLength < sizeof(FARFileHeader)) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const FARFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerLength - sizeof(FARFileHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + FARFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -108,14 +148,20 @@ file.Rewind(); FARFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 - || memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) - || !file.LengthIsAtLeast(fileHeader.headerLength)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_gdm.cpp =================================================================== --- soundlib/Load_gdm.cpp (revision 8886) +++ soundlib/Load_gdm.cpp (working copy) @@ -90,28 +90,61 @@ MPT_BINARY_STRUCT(GDMSampleHeader, 62) -bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static const MODTYPE gdmFormatOrigin[] = { - file.Rewind(); + MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM +}; - const MODTYPE gdmFormatOrigin[] = - { - MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM - }; - GDMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "GDM\xFE", 4) +static bool ValidateHeader(const GDMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "GDM\xFE", 4) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 - || memcmp(fileHeader.magic2, "GMFS", 4) + || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 - || fileHeader.originalFormat >= CountOf(gdmFormatOrigin) + || fileHeader.originalFormat >= mpt::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_imf.cpp =================================================================== --- soundlib/Load_imf.cpp (revision 8886) +++ soundlib/Load_imf.cpp (working copy) @@ -351,19 +351,84 @@ } } + +static bool ValidateHeader(const IMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.im10, "IM10", 4) + || fileHeader.ordNum > 256 + || fileHeader.insNum >= MAX_INSTRUMENTS + ) + { + return false; + } + bool channelFound = false; + for(uint8 chn = 0; chn < 32; chn++) + { + switch(fileHeader.channels[chn].status) + { + case 0: // enabled; don't worry about it + channelFound = true; + break; + case 1: // mute + channelFound = true; + break; + case 2: // disabled + // nothing + break; + default: // uhhhh.... freak out + return false; + } + } + if(!channelFound) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + IMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { IMFFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.im10, "IM10", 4) - || fileHeader.ordNum > 256 - || fileHeader.insNum >= MAX_INSTRUMENTS - || !file.CanRead(256)) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } // Read channel configuration std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled @@ -397,7 +462,9 @@ if(!detectedChannels) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_it.cpp =================================================================== --- soundlib/Load_it.cpp (revision 8886) +++ soundlib/Load_it.cpp (working copy) @@ -402,6 +402,43 @@ } +static bool ValidateHeader(const ITFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4)) + || fileHeader.insnum > 0xFF + || fileHeader.smpnum >= MAX_SAMPLES + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + ITFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -408,15 +445,20 @@ file.Rewind(); ITFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.id, "IMPM", 4) && memcmp(fileHeader.id, "tpm.", 4)) - || fileHeader.insnum > 0xFF - || fileHeader.smpnum >= MAX_SAMPLES - || !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_itp.cpp =================================================================== --- soundlib/Load_itp.cpp (revision 8886) +++ soundlib/Load_itp.cpp (working copy) @@ -60,6 +60,54 @@ MPT_BINARY_STRUCT(ITPModCommand, 6) +struct ITPHeader +{ + uint32le magic; + uint32le version; +}; + +MPT_BINARY_STRUCT(ITPHeader, 8) + + +static bool ValidateHeader(const ITPHeader &hdr) +//---------------------------------------------- +{ + if(hdr.magic != MAGIC4BE('.','i','t','p')) + { + return false; + } + if(hdr.version > 0x00000103 || hdr.version < 0x00000100) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr) +//---------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(hdr); + return 12 + 4 + 24 + 4 - sizeof(ITPHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + ITPHeader hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr)); +} + + bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------- { @@ -81,22 +129,29 @@ ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file }; - uint32 version, size; - file.Rewind(); - // Check file ID - if(!file.CanRead(12 + 4 + 24 + 4) - || file.ReadUint32LE() != MAGIC4BE('.','i','t','p') // Magic bytes - || (version = file.ReadUint32LE()) > 0x00000103 // Format version - || version < 0x00000100) + ITPHeader hdr; + if(!file.ReadStruct(hdr)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(hdr)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(hdr)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + uint32 version, size; + version = hdr.version; + InitializeGlobals(MOD_TYPE_IT); m_playBehaviour.reset(); file.ReadSizedString<uint32le, mpt::String::maybeNullTerminated>(m_songName); Index: soundlib/Load_mdl.cpp =================================================================== --- soundlib/Load_mdl.cpp (revision 8886) +++ soundlib/Load_mdl.cpp (working copy) @@ -422,18 +422,50 @@ } +static bool ValidateHeader(const MDLFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.id, "DMDL", 4) + || fileHeader.version >= 0x20) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MDLFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); MDLFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "DMDL", 4) - || fileHeader.version >= 0x20) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_med.cpp =================================================================== --- soundlib/Load_med.cpp (revision 8886) +++ soundlib/Load_med.cpp (working copy) @@ -493,22 +493,68 @@ } +static bool ValidateHeader(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------- +{ + if(std::memcmp(pmmh.id, "MMD", 3) + || pmmh.id[3] < '0' || pmmh.id[3] > '3' + || pmmh.song == 0 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(pmmh); + return sizeof(MMD0SONGHEADER); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MEDMODULEHEADER pmmh; + if(!file.ReadStruct(pmmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(pmmh)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh)); +} + + bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { file.Rewind(); MEDMODULEHEADER pmmh; - uint32 dwSong; - if(!file.CanRead(512) - || !file.ReadStruct(pmmh) - || memcmp(pmmh.id, "MMD", 3) - || pmmh.id[3] < '0' || pmmh.id[3] > '3' - || (dwSong = pmmh.song) == 0 - || !file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + if(!file.ReadStruct(pmmh)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(pmmh)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(pmmh)))) + { + return false; + } + const uint32 dwSong = pmmh.song; + if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mo3.cpp =================================================================== --- soundlib/Load_mo3.cpp (revision 8886) +++ soundlib/Load_mo3.cpp (working copy) @@ -691,26 +691,74 @@ #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +struct MO3ContainerHeader +{ + char magic[3]; // MO3 + uint8le version; + uint32le musicSize; +}; +MPT_BINARY_STRUCT(MO3ContainerHeader, 8) + + +static bool ValidateHeader(const MO3ContainerHeader &containerHeader) +//------------------------------------------------------------------- +{ + if(std::memcmp(containerHeader.magic, "MO3", 3)) + { + return false; + } + if(containerHeader.musicSize <= sizeof(MO3FileHeader)) + { + return false; + } + if(containerHeader.version > 5) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(containerHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); - if(!file.CanRead(12) || !file.ReadMagic("MO3")) + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) { return false; } - const uint8 version = file.ReadUint8(); - const uint32 musicSize = file.ReadUint32LE(); - if(musicSize <= sizeof(MO3FileHeader) || version > 5) + if(!ValidateHeader(containerHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } + const uint8 version = containerHeader.version; + const uint32 musicSize = containerHeader.musicSize; + uint32 compressedSize = uint32_max; if(version >= 5) { Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 8886) +++ soundlib/Load_mod.cpp (working copy) @@ -653,20 +653,20 @@ } -bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct MODMagicResult { - char magic[4]; - if(!file.Seek(1080) || !file.ReadArray(magic)) - { - return false; - } + CHANNELINDEX m_nChannels = 4; + mpt::ustring m_madeWithTracker = mpt::ustring(); + bool isNoiseTracker = false; + bool isStartrekker = false; + bool isGenericMultiChannel = false; + bool setMODVBlankTiming = false; +}; - InitializeGlobals(MOD_TYPE_MOD); - m_nChannels = 4; - bool isNoiseTracker = false, isStartrekker = false, isGenericMultiChannel = false; - // Check MOD Magic +static bool CheckMODMagic(const char magic[4], MODMagicResult *result) +{ + MODMagicResult *r = result; if(IsMagic(magic, "M.K.") // ProTracker and compatible || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) || IsMagic(magic, "PATT") // ProTracker 3.6 @@ -673,66 +673,143 @@ || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + } } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk || IsMagic(magic, "N.T.")) { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("NoiseTracker"); - isNoiseTracker = true; + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("NoiseTracker"); + r->isNoiseTracker = true; + } } else if(IsMagic(magic, "OKTA") || IsMagic(magic, "OCTA")) { // Oktalyzer - m_nChannels = 8; - m_madeWithTracker = MPT_USTRING("Oktalyzer"); + if(r) + { + r->m_nChannels = 8; + r->m_madeWithTracker = MPT_USTRING("Oktalyzer"); + } } else if(IsMagic(magic, "CD81") || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon - m_nChannels = magic[2] - '0'; - m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + if(r) + { + r->m_nChannels = magic[2] - '0'; + r->m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + } } else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8') { // Digital Tracker on Atari Falcon - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Digital Tracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Digital Tracker"); + } } else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9') { // FLTx / EXOx - Startrekker by Exolon / Fairlight - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Startrekker"); - isStartrekker = true; - m_playBehaviour.set(kMODVBlankTiming); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Startrekker"); + r->isStartrekker = true; + r->setMODVBlankTiming = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3)) { // xCHN - Many trackers - m_nChannels = magic[0] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = magic[0] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9' && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers - m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '4' && magic[3] <= '9') { // TDZx - TakeTracker - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("TakeTracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("TakeTracker"); + } } else { return false; } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1080 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1080); + char magic[4]; + file.ReadArray(magic); + if(!CheckMODMagic(magic, nullptr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + char magic[4]; + if(!file.Seek(1080) || !file.ReadArray(magic)) + { + return false; + } + + InitializeGlobals(MOD_TYPE_MOD); + + MODMagicResult modMagicResult; + if(CheckMODMagic(magic, &modMagicResult)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) { return true; } + m_nChannels = modMagicResult.m_nChannels; + m_madeWithTracker = modMagicResult.m_madeWithTracker; + bool isNoiseTracker = modMagicResult.isNoiseTracker; + bool isStartrekker = modMagicResult.isStartrekker; + bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel; + if(modMagicResult.setMODVBlankTiming) + { + m_playBehaviour.set(kMODVBlankTiming); + } + LimitMax(m_nChannels, MAX_BASECHANNELS); // Startrekker 8 channel mod (needs special treatment, see below) @@ -1165,8 +1242,8 @@ // Check if a name string is valid (i.e. doesn't contain binary garbage data) template<size_t N> -static uint32 CountInvalidChars(char (&name)[N]) -//---------------------------------------------- +static uint32 CountInvalidChars(const char (&name)[N]) +//---------------------------------------------------- { uint32 invalidChars = 0; for(auto c : name) @@ -1194,25 +1271,114 @@ }; -bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- + +struct M15FileHeaders { - file.Rewind(); + char songname[20]; + MODSampleHeader sampleHeaders[15]; + MODFileHeader fileHeader; +}; - char songname[20]; - file.ReadArray(songname); +MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) + +static bool ValidateHeader(const M15FileHeaders &fileHeaders) +{ // In theory, sample and song names should only ever contain printable ASCII chars and null. // However, there are quite a few SoundTracker modules in the wild with random // characters. To still be able to distguish them from other formats, we just reject // files with *too* many bogus characters. Arbitrary threshold: 20 bogus characters in total // or more than 5 invalid characters just in the title alone. - uint32 invalidChars = CountInvalidChars(songname); - if(invalidChars > 5 || !file.CanRead(sizeof(MODSampleHeader) * 15 + sizeof(MODFileHeader))) + uint32 invalidChars = CountInvalidChars(fileHeaders.songname); + if(invalidChars > 5) { return false; } + SmpLength totalSampleLen = 0; + uint8 allVolumes = 0; + + for(SAMPLEINDEX smp = 0; smp < 15; smp++) + { + const MODSampleHeader &sampleHeader = fileHeaders.sampleHeaders[smp]; + + invalidChars += CountInvalidChars(sampleHeader.name); + + // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) + if(invalidChars > 48 + || sampleHeader.volume > 64 + || sampleHeader.finetune != 0 + || sampleHeader.length > 32768) + { + return false; + } + + totalSampleLen += sampleHeader.length; + allVolumes |= sampleHeader.volume; + + } + + // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) + if(totalSampleLen == 0 || allVolumes == 0) + { + return false; + } + + // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. + // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. + if(fileHeaders.fileHeader.numOrders > 128 || fileHeaders.fileHeader.restartPos > 220) + { + return false; + } + + for(uint8 ord : fileHeaders.fileHeader.orderList) + { + // Sanity check: 64 patterns max. + if(ord > 63) + { + return false; + } + } + + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeaders)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return false; + } + if(!ValidateHeader(fileHeaders)) + { + return false; + } + + char songname[20]; + std::memcpy(songname, fileHeaders.songname, 20); + InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); @@ -1223,26 +1389,15 @@ bool hasDiskNames = true; SmpLength totalSampleLen = 0; - uint8 allVolumes = 0; m_nSamples = 15; + file.Seek(20); for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { MODSampleHeader sampleHeader; ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true); - invalidChars += CountInvalidChars(sampleHeader.name); - // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) - if(invalidChars > 48 - || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) - { - return false; - } - totalSampleLen += Samples[smp].nLength; - allVolumes |= sampleHeader.volume; if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':')) { @@ -1263,25 +1418,9 @@ minVersion = std::max(minVersion, MST1_00); } - // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) - if(totalSampleLen == 0 || allVolumes == 0) - return false; - MODFileHeader fileHeader; file.ReadStruct(fileHeader); - // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. - // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. - if(fileHeader.numOrders > 128 || fileHeader.restartPos > 220) - return false; - - for(uint8 ord : fileHeader.orderList) - { - // Sanity check: 64 patterns max. - if(ord > 63) - return false; - } - ReadOrderFromArray(Order(), fileHeader.orderList); PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); @@ -1578,6 +1717,55 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1464 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1464); + char magic[4]; + file.ReadArray(magic); + if(!IsMagic(magic, "MTN\0") && !IsMagic(magic, "IT10")) + { + return ProbeFailure; + } + file.Seek(20); + uint32 invalidChars = 0; + for(SAMPLEINDEX smp = 1; smp <= 31; smp++) + { + MODSampleHeader sampleHeader; + if(!file.ReadStruct(sampleHeader)) + { + return ProbeWantMoreData; + } + invalidChars += CountInvalidChars(sampleHeader.name); + } + if(invalidChars > 256) + { + return ProbeFailure; + } + const uint8 numOrders = file.ReadUint8(); + const uint8 numTracks = file.ReadUint8(); + if(numOrders > 128) + { + return ProbeFailure; + } + uint8 tracks[128 * 4]; + file.ReadArray(tracks); + for(auto track : tracks) + { + if(track > numTracks) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // SoundTracker 2.6 / Ice Tracker variation of the MOD format // The only real difference to other SoundTracker formats is the way patterns are stored: // Every pattern consists of four independent, re-usable tracks. @@ -1596,11 +1784,15 @@ m_playBehaviour.set(kMODSampleSwap); // untested if(IsMagic(magic, "MTN\0")) + { m_madeWithTracker = MPT_USTRING("SoundTracker 2.6"); - else if(IsMagic(magic, "IT10")) + } else if(IsMagic(magic, "IT10")) + { m_madeWithTracker = MPT_USTRING("Ice Tracker 1.0 / 1.1"); - else + } else + { return false; + } // Reading song title file.Seek(0); @@ -1622,7 +1814,9 @@ const uint8 numOrders = file.ReadUint8(); const uint8 numTracks = file.ReadUint8(); if(numOrders > 128) + { return false; + } uint8 tracks[128 * 4]; file.ReadArray(tracks); @@ -1629,11 +1823,15 @@ for(auto track : tracks) { if(track > numTracks) + { return false; + } } if(loadFlags == onlyVerifyHeader) + { return true; + } // Now we can be pretty sure that this is a valid MOD file. Set up default song settings. m_nChannels = 4; @@ -1731,6 +1929,48 @@ +struct PT36Header +{ + char magicFORM[4]; // "FORM" + uint8be dummy1[4]; + char magicMODL[4]; // "MODL" +}; + +MPT_BINARY_STRUCT(PT36Header, 12); + + +static bool ValidateHeader(const PT36Header &fileHeader) +//------------------------------------------------------ +{ + if(std::memcmp(fileHeader.magicFORM, "FORM", 4)) + { + return false; + } + if(std::memcmp(fileHeader.magicMODL, "MODL", 4)) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // ProTracker 3.6 version of the MOD format // Basically just a normal ProTracker mod with different magic, wrapped in an IFF file. // The "PTDT" chunk is passed to the normal MOD loader. @@ -1738,16 +1978,20 @@ //-------------------------------------------------------------------- { file.Rewind(); - if(!file.ReadMagic("FORM") - || !file.Skip(4) - || !file.ReadMagic("MODL")) + + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; } - + if(!ValidateHeader(fileHeader)) + { + return false; + } + bool ok = false, infoOk = false; FileReader commentChunk; - mpt::ustring version = MPT_USTRING("3.6"); + mpt::ustring version; PT36InfoChunk info; MemsetZero(info); @@ -1798,6 +2042,11 @@ break; } } while(file.ReadStruct(iffHead)); + + if(version.empty()) + { + version = MPT_USTRING("3.6"); + } // both an info chunk and a module are required if(ok && infoOk) Index: soundlib/Load_mt2.cpp =================================================================== --- soundlib/Load_mt2.cpp (revision 8886) +++ soundlib/Load_mt2.cpp (working copy) @@ -395,23 +395,66 @@ } -bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MT2FileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MT2FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MT20", 4) + if(std::memcmp(fileHeader.signature, "MT20", 4) || fileHeader.version < 0x200 || fileHeader.version >= 0x300 || fileHeader.numChannels < 1 || fileHeader.numChannels > 64 || fileHeader.numOrders > 256 || fileHeader.numInstruments >= MAX_INSTRUMENTS || fileHeader.numSamples >= MAX_SAMPLES - || !file.CanRead(256)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MT2FileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mtm.cpp =================================================================== --- soundlib/Load_mtm.cpp (revision 8886) +++ soundlib/Load_mtm.cpp (working copy) @@ -75,23 +75,65 @@ MPT_BINARY_STRUCT(MTMSampleHeader, 37) -bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "MTM", 3) + if(std::memcmp(fileHeader.id, "MTM", 3) || fileHeader.version >= 0x20 || fileHeader.lastOrder > 127 || fileHeader.beatsPerTrack > 64 || fileHeader.numChannels > 32 || fileHeader.numChannels == 0 - || !file.CanRead(sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_okt.cpp =================================================================== --- soundlib/Load_okt.cpp (revision 8886) +++ soundlib/Load_okt.cpp (working copy) @@ -258,6 +258,35 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(8)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("OKTASONG")) + { + return ProbeFailure; + } + OktIffChunk iffHead; + if(!file.ReadStruct(iffHead)) + { + return ProbeWantMoreData; + } + if(iffHead.chunksize == 0) + { + return ProbeFailure; + } + if((iffHead.signature & 0x7f7f7f7fu) != iffHead.signature) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { Index: soundlib/Load_plm.cpp =================================================================== --- soundlib/Load_plm.cpp (revision 8886) +++ soundlib/Load_plm.cpp (working copy) @@ -83,6 +83,44 @@ MPT_BINARY_STRUCT(PLMOrderItem, 4) +static bool ValidateHeader(const PLMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "PLM\x1A", 4) + || fileHeader.version != 0x10 + || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 + || fileHeader.headerSize < sizeof(PLMFileHeader) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PLMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -89,19 +127,28 @@ file.Rewind(); PLMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PLM\x1A", 4) - || fileHeader.version != 0x10 - || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 - || !file.Seek(fileHeader.headerSize) - || !file.CanRead(4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples))) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + if(!file.Seek(fileHeader.headerSize)) + { + return false; + } + InitializeGlobals(MOD_TYPE_PLM); InitializeChannels(); m_SongFlags = SONG_ITOLDEFFECTS; Index: soundlib/Load_psm.cpp =================================================================== --- soundlib/Load_psm.cpp (revision 8886) +++ soundlib/Load_psm.cpp (working copy) @@ -208,10 +208,51 @@ offset = 0; } return ConvertStrTo<uint16>(&patternID[offset]); +} + +static bool ValidateHeader(const PSMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.formatID, "PSM ", 4) + || std::memcmp(fileHeader.fileInfoID, "FILE", 4)) + { + return false; + } + return true; } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PSMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + PSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(chunkHeader.length == 0) + { + return ProbeFailure; + } + if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -244,8 +285,7 @@ #endif // MPT_PSM_DECRYPT // Check header - if(memcmp(fileHeader.formatID, "PSM ", 4) - || memcmp(fileHeader.fileInfoID, "FILE", 4)) + if(!ValidateHeader(fileHeader)) { return false; } @@ -1029,15 +1069,10 @@ MPT_BINARY_STRUCT(PSM16PatternHeader, 4) -bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) -//--------------------------------------------------------------------- +static bool ValidateHeader(const PSM16FileHeader &fileHeader) +//----------------------------------------------------------- { - file.Rewind(); - - // Is it a valid PSM16 file? - PSM16FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.formatID, "PSM\xFE", 4) + if(std::memcmp(fileHeader.formatID, "PSM\xFE", 4) || fileHeader.lineEnd != 0x1A || (fileHeader.formatVersion != 0x10 && fileHeader.formatVersion != 0x01) // why is this sometimes 0x01? || fileHeader.patternVersion != 0 // 255ch pattern version not supported (did anyone use this?) @@ -1047,8 +1082,45 @@ || std::max(fileHeader.numChannelsPlay, fileHeader.numChannelsReal) == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) +//--------------------------------------------------------------------- +{ + file.Rewind(); + + // Is it a valid PSM16 file? + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ptm.cpp =================================================================== --- soundlib/Load_ptm.cpp (revision 8886) +++ soundlib/Load_ptm.cpp (working copy) @@ -104,14 +104,10 @@ MPT_BINARY_STRUCT(PTMSampleHeader, 80) -bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const PTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - - PTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PTMF", 4) + if(std::memcmp(fileHeader.magic, "PTMF", 4) || fileHeader.dosEOF != 26 || fileHeader.versionHi > 2 || fileHeader.flags != 0 @@ -120,11 +116,57 @@ || !fileHeader.numOrders || fileHeader.numOrders > 256 || !fileHeader.numSamples || fileHeader.numSamples > 255 || !fileHeader.numPatterns || fileHeader.numPatterns > 128 - || !file.CanRead(fileHeader.numSamples * sizeof(PTMSampleHeader))) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.numSamples * sizeof(PTMSampleHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_s3m.cpp =================================================================== --- soundlib/Load_s3m.cpp (revision 8886) +++ soundlib/Load_s3m.cpp (working copy) @@ -172,6 +172,43 @@ }; +static bool ValidateHeader(const S3MFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "SCRM", 4) + || fileHeader.fileType != S3MFileHeader::idS3MType + || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + S3MFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -179,15 +216,20 @@ // Is it a valid S3M file? S3MFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || !file.CanRead(fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2) - || memcmp(fileHeader.magic, "SCRM", 4) - || fileHeader.fileType != S3MFileHeader::idS3MType - || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_sfx.cpp =================================================================== --- soundlib/Load_sfx.cpp (revision 8886) +++ soundlib/Load_sfx.cpp (working copy) @@ -96,6 +96,108 @@ return 0; } + +static bool ValidateHeader(const SFXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.numOrders > 128) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + + if(!file.CanRead(0x40)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x3c) && file.ReadMagic("SONG")) + { + file.Rewind(); + for(SAMPLEINDEX smp = 0; smp < 15; smp++) + { + if(file.ReadUint32BE() > 131072) + { + return ProbeFailure; + } + } + file.Skip(4); + if(!file.CanRead(2)) + { + return ProbeWantMoreData; + } + uint16 speed = file.ReadUint16BE(); + if(speed < 178) + { + return ProbeFailure; + } + if(!file.CanRead(sizeof(SFXSampleHeader) * 15)) + { + return ProbeWantMoreData; + } + file.Skip(sizeof(SFXSampleHeader) * 15); + SFXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeSuccess; + } + + if(!file.CanRead(0x80)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x3c) && file.ReadMagic("SO31")) + { + file.Rewind(); + for(SAMPLEINDEX smp = 0; smp < 31; smp++) + { + if(file.ReadUint32BE() > 131072) + { + return ProbeFailure; + } + } + file.Skip(4); + if(!file.CanRead(2)) + { + return ProbeWantMoreData; + } + uint16 speed = file.ReadUint16BE(); + if(speed < 178) + { + return ProbeFailure; + } + if(!file.CanRead(sizeof(SFXSampleHeader) * 31)) + { + return ProbeWantMoreData; + } + file.Skip(sizeof(SFXSampleHeader) * 31); + SFXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeSuccess; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeFailure; +} + + bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -164,12 +266,18 @@ } SFXFileHeader fileHeader; - file.ReadStruct(fileHeader); - - if(fileHeader.numOrders > 128) + if(!file.ReadStruct(fileHeader)) + { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) Index: soundlib/Load_stm.cpp =================================================================== --- soundlib/Load_stm.cpp (revision 8886) +++ soundlib/Load_stm.cpp (working copy) @@ -94,6 +94,49 @@ MPT_BINARY_STRUCT(STMPatternData, 4*64*4) +static bool ValidateHeader(const STMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.filetype != 2 + || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. + || fileHeader.verMajor != 2 + || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 + || fileHeader.globalVolume > 64 + || (std::memcmp(fileHeader.trackername, "!Scream!", 8) + && std::memcmp(fileHeader.trackername, "BMOD2STM", 8) + && std::memcmp(fileHeader.trackername, "WUZAMOD!", 8)) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const STMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 31 * sizeof(STMSampleHeader) + 128; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -105,20 +148,20 @@ // After reviewing all STM files on ModLand and ModArchive, it was found that the // case-insensitive comparison is most likely not necessary for any files in the wild. STMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.filetype != 2 - || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. - || fileHeader.verMajor != 2 - || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 - || fileHeader.globalVolume > 64 - || (memcmp(fileHeader.trackername, "!Scream!", 8) - && memcmp(fileHeader.trackername, "BMOD2STM", 8) - && memcmp(fileHeader.trackername, "WUZAMOD!", 8)) - || !file.CanRead(31 * sizeof(STMSampleHeader) + 128)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_stp.cpp =================================================================== --- soundlib/Load_stp.cpp (revision 8886) +++ soundlib/Load_stp.cpp (working copy) @@ -23,6 +23,7 @@ // File header (except for "STP3" magic) struct STPFileHeader { + char magic[4]; uint16be version; uint8be numOrders; uint8be patternLength; @@ -38,7 +39,7 @@ uint16be sampleStructSize; }; -MPT_BINARY_STRUCT(STPFileHeader, 200); +MPT_BINARY_STRUCT(STPFileHeader, 204); // Sample header (common part between all versions) @@ -203,24 +204,57 @@ } -bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const STPFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - if(!file.ReadMagic("STP3")) - return false; - - STPFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) + if(std::memcmp(fileHeader.magic, "STP3", 4) || fileHeader.version > 2 || fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES || fileHeader.timerCount == 0 || fileHeader.midiCount != 50) + { return false; + } + return true; +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } InitializeGlobals(MOD_TYPE_STP); Index: soundlib/Load_ult.cpp =================================================================== --- soundlib/Load_ult.cpp (revision 8886) +++ soundlib/Load_ult.cpp (working copy) @@ -336,21 +336,53 @@ }; +static bool ValidateHeader(const UltFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.version < '1' + || fileHeader.version > '4' + || std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UltFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + UltFileHeader fileHeader; - - // Tracker ID - if(!file.ReadStruct(fileHeader) - || fileHeader.version < '1' - || fileHeader.version > '4' - || memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_xm.cpp =================================================================== --- soundlib/Load_xm.cpp (revision 8886) +++ soundlib/Load_xm.cpp (working copy) @@ -262,6 +262,43 @@ DECLARE_FLAGSET(TrackerVersions) +static bool ValidateHeader(const XMFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if(fileHeader.channels == 0 + || fileHeader.channels > MAX_BASECHANNELS + || std::memcmp(fileHeader.signature, "Extended Module: ", 17) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const XMFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + XMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -268,13 +305,17 @@ file.Rewind(); XMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.channels == 0 - || fileHeader.channels > MAX_BASECHANNELS - || memcmp(fileHeader.signature, "Extended Module: ", 17) - || !file.CanRead(fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments))) + if(!file.ReadStruct(fileHeader)) { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; } else if(loadFlags == onlyVerifyHeader) { return true; Index: soundlib/Sndfile.cpp =================================================================== --- soundlib/Sndfile.cpp (revision 8886) +++ soundlib/Sndfile.cpp (working copy) @@ -214,9 +214,113 @@ const std::size_t CSoundFile::ProbeRecommendedSize = 2048; +CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize) +//------------------------------------------------------------------------------------------------------------------------------------ +{ + const uint64 availableFileSize = file.GetLength(); + const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); + //const uint64 validFileSize = std::min<uint64>(fileSize, ProbeRecommendedSize); + const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; + //const uint64 goalMinimumSize = std::min<uint64>(goalSize, ProbeRecommendedSize); + if(pfilesize) + { + if(availableFileSize < std::min<uint64>(fileSize, ProbeRecommendedSize)) + { + if(availableFileSize < goalSize) + { + return ProbeWantMoreData; + } + } else + { + if(fileSize < goalSize) + { + return ProbeFailure; + } + } + return ProbeSuccess; + } +#if 0 + if(!pfilesize) + { + if(fileSize < goalSize && fileSize < ProbeRecommendedSize) + { + return ProbeWantMoreData; + } + return ProbeSuccess; + } +#else + return ProbeSuccess; +#endif +} + + +#define MPT_DO_PROBE( storedResult , call ) \ + MPT_DO { \ + ProbeResult lastResult = call ; \ + if(lastResult == ProbeSuccess) { \ + return ProbeSuccess; \ + } else if(lastResult == ProbeWantMoreData) { \ + storedResult = ProbeWantMoreData; \ + } \ + } MPT_WHILE_0 \ +/**/ + + CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize) //------------------------------------------------------------------------------------------------------------------- { +#if 1 + ProbeResult result = ProbeFailure; + MemoryFileReader file(data); + if(flags & ProbeContainers) + { + MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize)); + } + if(flags & ProbeModules) + { + MPT_DO_PROBE(result, ProbeFileHeader669(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_Asylum(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_DSMI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDBM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDIGI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderFAR(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderGDM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderICE(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderITP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderJ2B(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderM15(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMDL(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMED(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMO3(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMOD(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMT2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderOKT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPLM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM16(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPT36(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderS3M(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSFX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderULT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXM(file, pfilesize)); + } + return result; +#else uint64 filesize = 0; if(pfilesize) { @@ -257,6 +361,7 @@ } sndFile->Destroy(); return ProbeSuccess; +#endif } Index: soundlib/Sndfile.h =================================================================== --- soundlib/Sndfile.h (revision 8886) +++ soundlib/Sndfile.h (working copy) @@ -579,6 +579,8 @@ ProbeWantMoreData = -1 }; + static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); + static ProbeResult Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize); public: @@ -670,6 +672,49 @@ bool InitChannel(CHANNELINDEX nChn); void InitAmigaResampler(); + static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize); + + static ProbeResult ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize); + // Module Loaders bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -692,7 +737,6 @@ bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMed(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMod(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -708,9 +752,11 @@ bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + + bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector<const char *> GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored Index: soundlib/UMXTools.cpp =================================================================== --- soundlib/UMXTools.cpp (revision 8886) +++ soundlib/UMXTools.cpp (working copy) @@ -17,8 +17,9 @@ // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. -int32 ReadUMXIndex(FileReader &chunk) -//----------------------------------- +template <typename Tfile> +static int32 ReadUMXIndexImpl(Tfile &chunk) +//----------------------------------------- { enum { @@ -55,10 +56,17 @@ return result; } +int32 ReadUMXIndex(FileReader &chunk) +//----------------------------------- +{ + return ReadUMXIndexImpl(chunk); +} + // Returns true if the given nme exists in the name table. -bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) -//--------------------------------------------------------------------------------------------- +template <typename TFile> +static bool FindUMXNameTableEntryImpl(TFile &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------- { if(!name) { @@ -77,7 +85,7 @@ { if(fileHeader.packageVersion >= 64) { - int32 length = ReadUMXIndex(file); + int32 length = ReadUMXIndexImpl(file); if(length <= 0) { continue; @@ -110,7 +118,19 @@ return result; } +bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} + + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion) //------------------------------------------------------------------------- Index: soundlib/UMXTools.h =================================================================== --- soundlib/UMXTools.h (revision 8886) +++ soundlib/UMXTools.h (working copy) @@ -38,6 +38,9 @@ // Returns true if the given nme exists in the name table. bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name); +// Returns true if the given nme exists in the name table. +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name); + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion); Index: soundlib/load_j2b.cpp =================================================================== --- soundlib/load_j2b.cpp (revision 8886) +++ soundlib/load_j2b.cpp (working copy) @@ -615,6 +615,66 @@ } +struct AMFFRiffChunkFormat +{ + uint32le format; +}; + +MPT_BINARY_STRUCT(AMFFRiffChunkFormat, 4) + + +static bool ValidateHeader(const AMFFRiffChunk &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.id != AMFFRiffChunk::idRIFF) + { + return false; + } + if(fileHeader.GetLength() < 8 + sizeof(AMFFMainChunk)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const AMFFRiffChunkFormat &formatHeader) +//----------------------------------------------------------------- +{ + if(formatHeader.format != AMFFRiffChunk::idAMFF || formatHeader.format != AMFFRiffChunk::idAM__) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + AMFFRiffChunk fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(formatHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -624,15 +684,23 @@ { return false; } - - if(fileHeader.id != AMFFRiffChunk::idRIFF) + if(!ValidateHeader(fileHeader)) { return false; } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return false; + } + if(!ValidateHeader(formatHeader)) + { + return false; + } bool isAM; // false: AMFF, true: AM - uint32 format = file.ReadUint32LE(); + uint32 format = formatHeader.format; if(format == AMFFRiffChunk::idAMFF) isAM = false; // "AMFF" else if(format == AMFFRiffChunk::idAM__) @@ -877,6 +945,64 @@ return true; } + +static bool ValidateHeader(const J2BFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "MUSE", 4) + || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) + && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) + ) + { + return false; + } + if(fileHeader.packedLength == 0) + { + return false; + } + if(fileHeader.fileLength != fileHeader.packedLength + sizeof(J2BFileHeader)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const J2BFileHeader &fileHeader, uint64 filesize) +//---------------------------------------------------------------------------------- +{ + if(filesize != fileHeader.fileLength) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + J2BFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(fileHeader, *pfilesize)) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -891,17 +1017,21 @@ file.Rewind(); J2BFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MUSE", 4) - || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) - && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) - || fileHeader.fileLength != file.GetLength() + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(fileHeader.fileLength != file.GetLength() || fileHeader.packedLength != file.BytesLeft() - || fileHeader.packedLength == 0 ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } probe-refactor-v18.patch (110,517 bytes)
Index: libopenmpt/libopenmpt_version.h =================================================================== --- libopenmpt/libopenmpt_version.h (revision 8929) +++ libopenmpt/libopenmpt_version.h (working copy) @@ -21,7 +21,7 @@ /*! \brief libopenmpt patch version number */ #define OPENMPT_API_VERSION_PATCH 0 /*! \brief libopenmpt pre-release tag */ -#define OPENMPT_API_VERSION_PREREL "-pre.7" +#define OPENMPT_API_VERSION_PREREL "-pre.8" /*! \brief libopenmpt pre-release flag */ #define OPENMPT_API_VERSION_IS_PREREL 1 Index: libopenmpt/libopenmpt_version.mk =================================================================== --- libopenmpt/libopenmpt_version.mk (revision 8929) +++ libopenmpt/libopenmpt_version.mk (working copy) @@ -1,7 +1,7 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=3 LIBOPENMPT_VERSION_PATCH=0 -LIBOPENMPT_VERSION_PREREL=-pre.7 +LIBOPENMPT_VERSION_PREREL=-pre.8 LIBOPENMPT_LTVER_CURRENT=1 LIBOPENMPT_LTVER_REVISION=0 Index: soundlib/ContainerMMCMP.cpp =================================================================== --- soundlib/ContainerMMCMP.cpp (revision 8929) +++ soundlib/ContainerMMCMP.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -144,6 +145,66 @@ } +static bool ValidateHeader(const MMCMPFILEHEADER &mfh) +//---------------------------------------------------- +{ + if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) + { + return false; + } + if(mfh.hdrsize != sizeof(MMCMPHEADER)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const MMCMPHEADER &mmh) +//------------------------------------------------ +{ + if(mmh.nblocks == 0) + { + return false; + } + if(mmh.filesize == 0) + { + return false; + } + if(mmh.filesize > 0x80000000) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + MMCMPFILEHEADER mfh; + if(!file.ReadStruct(mfh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mfh)) + { + return ProbeFailure; + } + MMCMPHEADER mmh; + if(!file.ReadStruct(mmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mmh)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------- { @@ -151,14 +212,23 @@ containerItems.clear(); MMCMPFILEHEADER mfh; - if(!file.ReadStruct(mfh)) return false; - if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) return false; - if(mfh.hdrsize != sizeof(MMCMPHEADER)) return false; + if(!file.ReadStruct(mfh)) + { + return false; + } + if(!ValidateHeader(mfh)) + { + return false; + } MMCMPHEADER mmh; - if(!file.ReadStruct(mmh)) return false; - if(mmh.nblocks == 0) return false; - if(mmh.filesize == 0) return false; - if(mmh.filesize > 0x80000000) return false; + if(!file.ReadStruct(mmh)) + { + return false; + } + if(!ValidateHeader(mmh)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; Index: soundlib/ContainerPP20.cpp =================================================================== --- soundlib/ContainerPP20.cpp (revision 8929) +++ soundlib/ContainerPP20.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -115,6 +116,50 @@ } +struct PP20header +{ + char magic[4]; // "PP20" + uint8be efficiency[4]; +}; + +MPT_BINARY_STRUCT(PP20header, 8) + + +static bool ValidateHeader(const PP20header &hdr) +//----------------------------------------------- +{ + if(std::memcmp(hdr.magic, "PP20", 4) != 0) + { + return false; + } + if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15 + || hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15 + || hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15 + || hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PP20header hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackPP20(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------ { @@ -121,20 +166,25 @@ file.Rewind(); containerItems.clear(); - if(!file.ReadMagic("PP20")) return false; - if(!file.CanRead(8)) return false; - uint8 efficiency[4]; - file.ReadArray(efficiency); - if(efficiency[0] < 9 || efficiency[0] > 15 - || efficiency[1] < 9 || efficiency[1] > 15 - || efficiency[2] < 9 || efficiency[2] > 15 - || efficiency[3] < 9 || efficiency[3] > 15) + PP20header hdr; + if(!file.ReadStruct(hdr)) + { return false; + } + if(!ValidateHeader(hdr)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } + if(!file.CanRead(4)) + { + return false; + } + containerItems.emplace_back(); containerItems.back().data_cache = mpt::make_unique<std::vector<char> >(); std::vector<char> & unpackedData = *(containerItems.back().data_cache); Index: soundlib/ContainerUMX.cpp =================================================================== --- soundlib/ContainerUMX.cpp (revision 8929) +++ soundlib/ContainerUMX.cpp (working copy) @@ -12,11 +12,48 @@ #include "Loaders.h" #include "UMXTools.h" #include "Container.h" +#include "Sndfile.h" OPENMPT_NAMESPACE_BEGIN +static bool ValidateHeader(const UMXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) + || fileHeader.nameCount == 0 + || fileHeader.exportCount == 0 + || fileHeader.importCount == 0 + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UMXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(!FindUMXNameTableEntryMemory(file, fileHeader, "music")) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -24,15 +61,14 @@ containerItems.clear(); UMXFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) - || fileHeader.nameCount == 0 - || fileHeader.exportCount == 0 - || fileHeader.importCount == 0 - ) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } // Note that this can be a false positive, e.g. Unreal maps will have music and sound // in their name table because they usually import such files. However, it spares us Index: soundlib/ContainerXPK.cpp =================================================================== --- soundlib/ContainerXPK.cpp (revision 8929) +++ soundlib/ContainerXPK.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -329,6 +330,68 @@ } +static bool ValidateHeader(const XPKFILEHEADER &header) +//----------------------------------------------------- +{ + if(std::memcmp(header.XPKF, "XPKF", 4) != 0) + { + return false; + } + if(std::memcmp(header.SQSH, "SQSH", 4) != 0) + { + return false; + } + if(header.SrcLen == 0) + { + return false; + } + if(header.DstLen == 0) + { + return false; + } + MPT_STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); + if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize) +//------------------------------------------------------------------------------ +{ + if(filesize < header.SrcLen - 8) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + XPKFILEHEADER header; + if(!file.ReadStruct(header)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(header)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(header, *pfilesize)) + { + return ProbeFailure; + } + } + return ProbeSuccess; +} + + bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -336,19 +399,24 @@ containerItems.clear(); XPKFILEHEADER header; - if(!file.ReadStruct(header)) return false; - if(std::memcmp(header.XPKF, "XPKF", 4) != 0) return false; - if(std::memcmp(header.SQSH, "SQSH", 4) != 0) return false; - if(header.SrcLen == 0) return false; - if(header.DstLen == 0) return false; - STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); - if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) return false; + if(!file.ReadStruct(header)) + { + return false; + } + if(!ValidateHeader(header)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } - if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) return false; + if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) + { + return false; + } + containerItems.emplace_back(); containerItems.back().data_cache = mpt::make_unique<std::vector<char> >(); std::vector<char> & unpackedData = *(containerItems.back().data_cache); Index: soundlib/Load_669.cpp =================================================================== --- soundlib/Load_669.cpp (revision 8929) +++ soundlib/Load_669.cpp (working copy) @@ -62,14 +62,9 @@ MPT_BINARY_STRUCT(_669Sample, 25) -bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const _669FileHeader &fileHeader) { - _669FileHeader fileHeader; - - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.magic, "if", 2) && memcmp(fileHeader.magic, "JN", 2)) + if((std::memcmp(fileHeader.magic, "if", 2) && std::memcmp(fileHeader.magic, "JN", 2)) || fileHeader.samples > 64 || fileHeader.restartPos >= 128 || fileHeader.patterns > 128) @@ -76,21 +71,68 @@ { return false; } - - for(size_t i = 0; i < CountOf(fileHeader.breaks); i++) + for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++) { if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE) + { return false; + } if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0) + { return false; + } if(fileHeader.breaks[i] >= 64) + { return false; + } } + return true; +} + +static uint64 GetHeaderMinimumAdditionalSize(const _669FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + + file.Rewind(); + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) { return true; - } else if(!file.CanRead(fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u)) + } + + if(!file.CanRead(GetHeaderMinimumAdditionalSize(fileHeader))) { return false; } Index: soundlib/Load_amf.cpp =================================================================== --- soundlib/Load_amf.cpp (revision 8929) +++ soundlib/Load_amf.cpp (working copy) @@ -80,6 +80,42 @@ MPT_BINARY_STRUCT(AMFFileHeader, 41) +static bool ValidateHeader(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------ +{ + if(std::memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) + || fileHeader.numSamples > 64 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------------------------ +{ + return 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------------- +{ + AsylumFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------------- { @@ -86,14 +122,20 @@ file.Rewind(); AsylumFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) - || fileHeader.numSamples > 64 - || !file.CanRead(256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -341,6 +383,37 @@ } +static bool ValidateHeader(const AMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.amf, "AMF", 3) + || fileHeader.version < 8 || fileHeader.version > 14 + || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------------- +{ + AMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------ { @@ -347,14 +420,16 @@ file.Rewind(); AMFFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.amf, "AMF", 3) - || fileHeader.version < 8 || fileHeader.version > 14 - || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ams.cpp =================================================================== --- soundlib/Load_ams.cpp (revision 8929) +++ soundlib/Load_ams.cpp (working copy) @@ -332,21 +332,76 @@ MPT_BINARY_STRUCT(AMSSampleHeader, 17) +static bool ValidateHeader(const AMSFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.versionHigh != 0x01) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("Extreme")) + { + return ProbeFailure; + } + AMSFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + if(!file.ReadMagic("Extreme")) + { + return false; + } AMSFileHeader fileHeader; - if(!file.ReadMagic("Extreme") - || !file.ReadStruct(fileHeader) - || !file.Skip(fileHeader.extraSize) - || !file.CanRead(3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u) - || fileHeader.versionHigh != 0x01) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(!file.Skip(fileHeader.extraSize)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -641,30 +696,90 @@ MPT_BINARY_STRUCT(AMS2Description, 11) +static bool ValidateHeader(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------- +{ + if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("AMShdr\x1A")) + { + return ProbeFailure; + } + if(!file.CanRead(1)) + { + return ProbeWantMoreData; + } + const uint8 songNameLength = file.ReadUint8(); + if(!file.Skip(songNameLength)) + { + return ProbeWantMoreData; + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { file.Rewind(); - AMS2FileHeader fileHeader; if(!file.ReadMagic("AMShdr\x1A")) { return false; } - - InitializeGlobals(MOD_TYPE_AMS2); - - if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName) - || !file.ReadStruct(fileHeader) - || fileHeader.versionHigh != 2 || fileHeader.versionLow > 2 - || !file.CanRead(36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u)) + if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + InitializeGlobals(MOD_TYPE_AMS2); + m_nInstruments = fileHeader.numIns; m_nChannels = 32; SetupMODPanning(true); Index: soundlib/Load_dbm.cpp =================================================================== --- soundlib/Load_dbm.cpp (revision 8929) +++ soundlib/Load_dbm.cpp (working copy) @@ -293,19 +293,51 @@ } +static bool ValidateHeader(const DBMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.dbm0, "DBM0", 4) + || fileHeader.trkVerHi > 3) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { - DBMFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.dbm0, "DBM0", 4) - || fileHeader.trkVerHi > 3) + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_digi.cpp =================================================================== --- soundlib/Load_digi.cpp (revision 8929) +++ soundlib/Load_digi.cpp (working copy) @@ -73,6 +73,36 @@ } +static bool ValidateHeader(const DIGIFileHeader &fileHeader) +{ + if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20) + || !fileHeader.numChannels + || fileHeader.numChannels > 8 + || fileHeader.lastOrdIndex > 127) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + DIGIFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { @@ -79,15 +109,16 @@ file.Rewind(); DIGIFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DIGI Booster module\0", 20) - || !fileHeader.numChannels - || fileHeader.numChannels > 8 - || fileHeader.lastOrdIndex > 127) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dmf.cpp =================================================================== --- soundlib/Load_dmf.cpp (revision 8929) +++ soundlib/Load_dmf.cpp (working copy) @@ -887,18 +887,51 @@ } +static bool ValidateHeader(const DMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "DDMF", 4) + || !fileHeader.version || fileHeader.version > 10) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { + file.Rewind(); + DMFFileHeader fileHeader; - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DDMF", 4) - || !fileHeader.version || fileHeader.version > 10) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dsm.cpp =================================================================== --- soundlib/Load_dsm.cpp (revision 8929) +++ soundlib/Load_dsm.cpp (working copy) @@ -100,40 +100,100 @@ MPT_BINARY_STRUCT(DSMSampleHeader, 64) -bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct DSMHeader { - file.Rewind(); + char fileMagic0[4]; + char fileMagic1[4]; + char fileMagic2[4]; +}; - char fileMagic0[4], fileMagic1[4], fileMagic2[4]; - if(!file.ReadArray(fileMagic0)) return false; - if(!file.ReadArray(fileMagic1)) return false; - if(!file.ReadArray(fileMagic2)) return false; +MPT_BINARY_STRUCT(DSMHeader, 12) - if(!memcmp(fileMagic0, "RIFF", 4) - && !memcmp(fileMagic2, "DSMF", 4)) + +static bool ValidateHeader(const DSMHeader &fileHeader) +//----------------------------------------------------- +{ + if(!std::memcmp(fileHeader.fileMagic0, "RIFF", 4) + && !std::memcmp(fileHeader.fileMagic2, "DSMF", 4)) { // "Normal" DSM files with RIFF header // <RIFF> <file size> <DSMF> - } else if(!memcmp(fileMagic0, "DSMF", 4)) + return true; + } else if(!std::memcmp(fileHeader.fileMagic0, "DSMF", 4)) { // DSM files with alternative header // <DSMF> <4 bytes, usually 4x NUL or RIFF> <file size> <4 bytes, usually DSMF but not always> - file.Skip(4); + return true; } else { return false; } +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + if(!file.Skip(4)) + { + return ProbeWantMoreData; + } + } DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(std::memcmp(chunkHeader.magic, "SONG", 4)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} - file.ReadStruct(chunkHeader); + +bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + file.Skip(4); + } + DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return false; + } // Technically, the song chunk could be anywhere in the file, but we're going to simplify // things by not using a chunk header here and just expect it to be right at the beginning. - if(memcmp(chunkHeader.magic, "SONG", 4)) + if(std::memcmp(chunkHeader.magic, "SONG", 4)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_dtm.cpp =================================================================== --- soundlib/Load_dtm.cpp (revision 8929) +++ soundlib/Load_dtm.cpp (working copy) @@ -181,6 +181,37 @@ MPT_BINARY_STRUCT(DTMText, 12) +static bool ValidateHeader(const DTMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "D.T.", 4) + || fileHeader.headerSize < sizeof(fileHeader) - 8u + || fileHeader.headerSize > 256 // Excessively long song title? + || fileHeader.type != 0) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -187,15 +218,16 @@ file.Rewind(); DTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "D.T.", 4) - || fileHeader.headerSize < sizeof(fileHeader) - 8u - || fileHeader.headerSize > 256 // Excessively long song title? - || fileHeader.type != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_far.cpp =================================================================== --- soundlib/Load_far.cpp (revision 8929) +++ soundlib/Load_far.cpp (working copy) @@ -102,6 +102,46 @@ MPT_BINARY_STRUCT(FARSampleHeader, 48) +static bool ValidateHeader(const FARFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 + || std::memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) + ) + { + return false; + } + if(fileHeader.headerLength < sizeof(FARFileHeader)) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const FARFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerLength - sizeof(FARFileHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + FARFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -108,14 +148,20 @@ file.Rewind(); FARFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 - || memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) - || !file.LengthIsAtLeast(fileHeader.headerLength)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_gdm.cpp =================================================================== --- soundlib/Load_gdm.cpp (revision 8929) +++ soundlib/Load_gdm.cpp (working copy) @@ -90,28 +90,61 @@ MPT_BINARY_STRUCT(GDMSampleHeader, 62) -bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static const MODTYPE gdmFormatOrigin[] = { - file.Rewind(); + MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM +}; - const MODTYPE gdmFormatOrigin[] = - { - MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM - }; - GDMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "GDM\xFE", 4) +static bool ValidateHeader(const GDMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "GDM\xFE", 4) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 - || memcmp(fileHeader.magic2, "GMFS", 4) + || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 - || fileHeader.originalFormat >= CountOf(gdmFormatOrigin) + || fileHeader.originalFormat >= mpt::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_imf.cpp =================================================================== --- soundlib/Load_imf.cpp (revision 8929) +++ soundlib/Load_imf.cpp (working copy) @@ -351,19 +351,84 @@ } } + +static bool ValidateHeader(const IMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.im10, "IM10", 4) + || fileHeader.ordNum > 256 + || fileHeader.insNum >= MAX_INSTRUMENTS + ) + { + return false; + } + bool channelFound = false; + for(uint8 chn = 0; chn < 32; chn++) + { + switch(fileHeader.channels[chn].status) + { + case 0: // enabled; don't worry about it + channelFound = true; + break; + case 1: // mute + channelFound = true; + break; + case 2: // disabled + // nothing + break; + default: // uhhhh.... freak out + return false; + } + } + if(!channelFound) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + IMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { IMFFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.im10, "IM10", 4) - || fileHeader.ordNum > 256 - || fileHeader.insNum >= MAX_INSTRUMENTS - || !file.CanRead(256)) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } // Read channel configuration std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled @@ -397,7 +462,9 @@ if(!detectedChannels) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_it.cpp =================================================================== --- soundlib/Load_it.cpp (revision 8929) +++ soundlib/Load_it.cpp (working copy) @@ -402,6 +402,43 @@ } +static bool ValidateHeader(const ITFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4)) + || fileHeader.insnum > 0xFF + || fileHeader.smpnum >= MAX_SAMPLES + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + ITFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -408,15 +445,20 @@ file.Rewind(); ITFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.id, "IMPM", 4) && memcmp(fileHeader.id, "tpm.", 4)) - || fileHeader.insnum > 0xFF - || fileHeader.smpnum >= MAX_SAMPLES - || !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_itp.cpp =================================================================== --- soundlib/Load_itp.cpp (revision 8929) +++ soundlib/Load_itp.cpp (working copy) @@ -60,6 +60,54 @@ MPT_BINARY_STRUCT(ITPModCommand, 6) +struct ITPHeader +{ + uint32le magic; + uint32le version; +}; + +MPT_BINARY_STRUCT(ITPHeader, 8) + + +static bool ValidateHeader(const ITPHeader &hdr) +//---------------------------------------------- +{ + if(hdr.magic != MAGIC4BE('.','i','t','p')) + { + return false; + } + if(hdr.version > 0x00000103 || hdr.version < 0x00000100) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr) +//---------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(hdr); + return 12 + 4 + 24 + 4 - sizeof(ITPHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + ITPHeader hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr)); +} + + bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------- { @@ -81,22 +129,29 @@ ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file }; - uint32 version, size; - file.Rewind(); - // Check file ID - if(!file.CanRead(12 + 4 + 24 + 4) - || file.ReadUint32LE() != MAGIC4BE('.','i','t','p') // Magic bytes - || (version = file.ReadUint32LE()) > 0x00000103 // Format version - || version < 0x00000100) + ITPHeader hdr; + if(!file.ReadStruct(hdr)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(hdr)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(hdr)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + uint32 version, size; + version = hdr.version; + InitializeGlobals(MOD_TYPE_IT); m_playBehaviour.reset(); file.ReadSizedString<uint32le, mpt::String::maybeNullTerminated>(m_songName); Index: soundlib/Load_mdl.cpp =================================================================== --- soundlib/Load_mdl.cpp (revision 8929) +++ soundlib/Load_mdl.cpp (working copy) @@ -422,18 +422,50 @@ } +static bool ValidateHeader(const MDLFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.id, "DMDL", 4) + || fileHeader.version >= 0x20) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MDLFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); MDLFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "DMDL", 4) - || fileHeader.version >= 0x20) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_med.cpp =================================================================== --- soundlib/Load_med.cpp (revision 8929) +++ soundlib/Load_med.cpp (working copy) @@ -493,22 +493,68 @@ } +static bool ValidateHeader(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------- +{ + if(std::memcmp(pmmh.id, "MMD", 3) + || pmmh.id[3] < '0' || pmmh.id[3] > '3' + || pmmh.song == 0 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(pmmh); + return sizeof(MMD0SONGHEADER); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MEDMODULEHEADER pmmh; + if(!file.ReadStruct(pmmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(pmmh)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh)); +} + + bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { file.Rewind(); MEDMODULEHEADER pmmh; - uint32 dwSong; - if(!file.CanRead(512) - || !file.ReadStruct(pmmh) - || memcmp(pmmh.id, "MMD", 3) - || pmmh.id[3] < '0' || pmmh.id[3] > '3' - || (dwSong = pmmh.song) == 0 - || !file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + if(!file.ReadStruct(pmmh)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(pmmh)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(pmmh)))) + { + return false; + } + const uint32 dwSong = pmmh.song; + if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mo3.cpp =================================================================== --- soundlib/Load_mo3.cpp (revision 8929) +++ soundlib/Load_mo3.cpp (working copy) @@ -691,26 +691,74 @@ #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +struct MO3ContainerHeader +{ + char magic[3]; // MO3 + uint8le version; + uint32le musicSize; +}; +MPT_BINARY_STRUCT(MO3ContainerHeader, 8) + + +static bool ValidateHeader(const MO3ContainerHeader &containerHeader) +//------------------------------------------------------------------- +{ + if(std::memcmp(containerHeader.magic, "MO3", 3)) + { + return false; + } + if(containerHeader.musicSize <= sizeof(MO3FileHeader)) + { + return false; + } + if(containerHeader.version > 5) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(containerHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); - if(!file.CanRead(12) || !file.ReadMagic("MO3")) + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) { return false; } - const uint8 version = file.ReadUint8(); - const uint32 musicSize = file.ReadUint32LE(); - if(musicSize <= sizeof(MO3FileHeader) || version > 5) + if(!ValidateHeader(containerHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } + const uint8 version = containerHeader.version; + const uint32 musicSize = containerHeader.musicSize; + uint32 compressedSize = uint32_max; if(version >= 5) { Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 8929) +++ soundlib/Load_mod.cpp (working copy) @@ -654,20 +654,20 @@ } -bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct MODMagicResult { - char magic[4]; - if(!file.Seek(1080) || !file.ReadArray(magic)) - { - return false; - } + CHANNELINDEX m_nChannels = 4; + mpt::ustring m_madeWithTracker = mpt::ustring(); + bool isNoiseTracker = false; + bool isStartrekker = false; + bool isGenericMultiChannel = false; + bool setMODVBlankTiming = false; +}; - InitializeGlobals(MOD_TYPE_MOD); - m_nChannels = 4; - bool isNoiseTracker = false, isStartrekker = false, isGenericMultiChannel = false; - // Check MOD Magic +static bool CheckMODMagic(const char magic[4], MODMagicResult *result) +{ + MODMagicResult *r = result; if(IsMagic(magic, "M.K.") // ProTracker and compatible || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) || IsMagic(magic, "PATT") // ProTracker 3.6 @@ -674,66 +674,143 @@ || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + } } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk || IsMagic(magic, "N.T.")) { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("NoiseTracker"); - isNoiseTracker = true; + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("NoiseTracker"); + r->isNoiseTracker = true; + } } else if(IsMagic(magic, "OKTA") || IsMagic(magic, "OCTA")) { // Oktalyzer - m_nChannels = 8; - m_madeWithTracker = MPT_USTRING("Oktalyzer"); + if(r) + { + r->m_nChannels = 8; + r->m_madeWithTracker = MPT_USTRING("Oktalyzer"); + } } else if(IsMagic(magic, "CD81") || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon - m_nChannels = magic[2] - '0'; - m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + if(r) + { + r->m_nChannels = magic[2] - '0'; + r->m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + } } else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8') { // Digital Tracker on Atari Falcon - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Digital Tracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Digital Tracker"); + } } else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9') { // FLTx / EXOx - Startrekker by Exolon / Fairlight - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Startrekker"); - isStartrekker = true; - m_playBehaviour.set(kMODVBlankTiming); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Startrekker"); + r->isStartrekker = true; + r->setMODVBlankTiming = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3)) { // xCHN - Many trackers - m_nChannels = magic[0] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = magic[0] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9' && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers - m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '4' && magic[3] <= '9') { // TDZx - TakeTracker - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("TakeTracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("TakeTracker"); + } } else { return false; } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1080 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1080); + char magic[4]; + file.ReadArray(magic); + if(!CheckMODMagic(magic, nullptr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + char magic[4]; + if(!file.Seek(1080) || !file.ReadArray(magic)) + { + return false; + } + + InitializeGlobals(MOD_TYPE_MOD); + + MODMagicResult modMagicResult; + if(!CheckMODMagic(magic, &modMagicResult)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) { return true; } + m_nChannels = modMagicResult.m_nChannels; + m_madeWithTracker = modMagicResult.m_madeWithTracker; + bool isNoiseTracker = modMagicResult.isNoiseTracker; + bool isStartrekker = modMagicResult.isStartrekker; + bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel; + if(modMagicResult.setMODVBlankTiming) + { + m_playBehaviour.set(kMODVBlankTiming); + } + LimitMax(m_nChannels, MAX_BASECHANNELS); // Startrekker 8 channel mod (needs special treatment, see below) @@ -1166,8 +1243,8 @@ // Check if a name string is valid (i.e. doesn't contain binary garbage data) template<size_t N> -static uint32 CountInvalidChars(char (&name)[N]) -//---------------------------------------------- +static uint32 CountInvalidChars(const char (&name)[N]) +//---------------------------------------------------- { uint32 invalidChars = 0; for(auto c : name) @@ -1195,25 +1272,127 @@ }; -bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- + +struct M15FileHeaders { - file.Rewind(); + char songname[20]; + MODSampleHeader sampleHeaders[15]; + MODFileHeader fileHeader; +}; - char songname[20]; - file.ReadArray(songname); +MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) + +static bool ValidateHeader(const M15FileHeaders &fileHeaders) +{ // In theory, sample and song names should only ever contain printable ASCII chars and null. // However, there are quite a few SoundTracker modules in the wild with random // characters. To still be able to distguish them from other formats, we just reject // files with *too* many bogus characters. Arbitrary threshold: 20 bogus characters in total // or more than 5 invalid characters just in the title alone. - uint32 invalidChars = CountInvalidChars(songname); - if(invalidChars > 5 || !file.CanRead(sizeof(MODSampleHeader) * 15 + sizeof(MODFileHeader))) + uint32 invalidChars = CountInvalidChars(fileHeaders.songname); + if(invalidChars > 5) { return false; } + SmpLength totalSampleLen = 0; + uint8 allVolumes = 0; + + for(SAMPLEINDEX smp = 0; smp < 15; smp++) + { + const MODSampleHeader &sampleHeader = fileHeaders.sampleHeaders[smp]; + + invalidChars += CountInvalidChars(sampleHeader.name); + + // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) + if(invalidChars > 48 + || sampleHeader.volume > 64 + || sampleHeader.finetune != 0 + || sampleHeader.length > 32768) + { + return false; + } + + totalSampleLen += sampleHeader.length; + allVolumes |= sampleHeader.volume; + + } + + // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) + if(totalSampleLen == 0 || allVolumes == 0) + { + return false; + } + + // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. + // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. + if(fileHeaders.fileHeader.numOrders > 128 || fileHeaders.fileHeader.restartPos > 220) + { + return false; + } + + for(uint8 ord : fileHeaders.fileHeader.orderList) + { + // Sanity check: 64 patterns max. + if(ord > 63) + { + return false; + } + } + + bool allPatternsNull = true; + for(uint8 pat : fileHeaders.fileHeader.orderList) + { + if(pat != 0 && pat < 128) + { + allPatternsNull = false; + } + } + if(fileHeaders.fileHeader.restartPos == 0 && fileHeaders.fileHeader.numOrders == 0 && allPatternsNull) + { + return false; + } + + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeaders)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return false; + } + if(!ValidateHeader(fileHeaders)) + { + return false; + } + + char songname[20]; + std::memcpy(songname, fileHeaders.songname, 20); + InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); @@ -1224,26 +1403,15 @@ bool hasDiskNames = true; SmpLength totalSampleLen = 0; - uint8 allVolumes = 0; m_nSamples = 15; + file.Seek(20); for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { MODSampleHeader sampleHeader; ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true); - invalidChars += CountInvalidChars(sampleHeader.name); - // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) - if(invalidChars > 48 - || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) - { - return false; - } - totalSampleLen += Samples[smp].nLength; - allVolumes |= sampleHeader.volume; if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':')) { @@ -1264,36 +1432,22 @@ minVersion = std::max(minVersion, MST1_00); } - // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) - if(totalSampleLen == 0 || allVolumes == 0) - return false; - MODFileHeader fileHeader; file.ReadStruct(fileHeader); - // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. - // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. - if(fileHeader.numOrders > 128 || fileHeader.restartPos > 220) - return false; + ReadOrderFromArray(Order(), fileHeader.orderList); + PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); - for(uint8 ord : fileHeader.orderList) + // Most likely just a file with lots of NULs at the start + if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) { - // Sanity check: 64 patterns max. - if(ord > 63) - return false; + return false; } - ReadOrderFromArray(Order(), fileHeader.orderList); - PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); - // Let's see if the file is too small (including some overhead for broken files like sll7.mod or ghostbus.mod) if(file.BytesLeft() + 65536 < numPatterns * 64u * 4u + totalSampleLen) return false; - // Most likely just a file with lots of NULs at the start - if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) - return false; - if(loadFlags == onlyVerifyHeader) return true; @@ -1579,6 +1733,55 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1464 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1464); + char magic[4]; + file.ReadArray(magic); + if(!IsMagic(magic, "MTN\0") && !IsMagic(magic, "IT10")) + { + return ProbeFailure; + } + file.Seek(20); + uint32 invalidChars = 0; + for(SAMPLEINDEX smp = 1; smp <= 31; smp++) + { + MODSampleHeader sampleHeader; + if(!file.ReadStruct(sampleHeader)) + { + return ProbeWantMoreData; + } + invalidChars += CountInvalidChars(sampleHeader.name); + } + if(invalidChars > 256) + { + return ProbeFailure; + } + const uint8 numOrders = file.ReadUint8(); + const uint8 numTracks = file.ReadUint8(); + if(numOrders > 128) + { + return ProbeFailure; + } + uint8 tracks[128 * 4]; + file.ReadArray(tracks); + for(auto track : tracks) + { + if(track > numTracks) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // SoundTracker 2.6 / Ice Tracker variation of the MOD format // The only real difference to other SoundTracker formats is the way patterns are stored: // Every pattern consists of four independent, re-usable tracks. @@ -1597,11 +1800,15 @@ m_playBehaviour.set(kMODSampleSwap); // untested if(IsMagic(magic, "MTN\0")) + { m_madeWithTracker = MPT_USTRING("SoundTracker 2.6"); - else if(IsMagic(magic, "IT10")) + } else if(IsMagic(magic, "IT10")) + { m_madeWithTracker = MPT_USTRING("Ice Tracker 1.0 / 1.1"); - else + } else + { return false; + } // Reading song title file.Seek(0); @@ -1623,7 +1830,9 @@ const uint8 numOrders = file.ReadUint8(); const uint8 numTracks = file.ReadUint8(); if(numOrders > 128) + { return false; + } uint8 tracks[128 * 4]; file.ReadArray(tracks); @@ -1630,11 +1839,15 @@ for(auto track : tracks) { if(track > numTracks) + { return false; + } } if(loadFlags == onlyVerifyHeader) + { return true; + } // Now we can be pretty sure that this is a valid MOD file. Set up default song settings. m_nChannels = 4; @@ -1732,6 +1945,48 @@ +struct PT36Header +{ + char magicFORM[4]; // "FORM" + uint8be dummy1[4]; + char magicMODL[4]; // "MODL" +}; + +MPT_BINARY_STRUCT(PT36Header, 12); + + +static bool ValidateHeader(const PT36Header &fileHeader) +//------------------------------------------------------ +{ + if(std::memcmp(fileHeader.magicFORM, "FORM", 4)) + { + return false; + } + if(std::memcmp(fileHeader.magicMODL, "MODL", 4)) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // ProTracker 3.6 version of the MOD format // Basically just a normal ProTracker mod with different magic, wrapped in an IFF file. // The "PTDT" chunk is passed to the normal MOD loader. @@ -1739,16 +1994,20 @@ //-------------------------------------------------------------------- { file.Rewind(); - if(!file.ReadMagic("FORM") - || !file.Skip(4) - || !file.ReadMagic("MODL")) + + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; } - + if(!ValidateHeader(fileHeader)) + { + return false; + } + bool ok = false, infoOk = false; FileReader commentChunk; - mpt::ustring version = MPT_USTRING("3.6"); + mpt::ustring version; PT36InfoChunk info; MemsetZero(info); @@ -1799,6 +2058,11 @@ break; } } while(file.ReadStruct(iffHead)); + + if(version.empty()) + { + version = MPT_USTRING("3.6"); + } // both an info chunk and a module are required if(ok && infoOk) Index: soundlib/Load_mt2.cpp =================================================================== --- soundlib/Load_mt2.cpp (revision 8929) +++ soundlib/Load_mt2.cpp (working copy) @@ -395,23 +395,66 @@ } -bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MT2FileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MT2FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MT20", 4) + if(std::memcmp(fileHeader.signature, "MT20", 4) || fileHeader.version < 0x200 || fileHeader.version >= 0x300 || fileHeader.numChannels < 1 || fileHeader.numChannels > 64 || fileHeader.numOrders > 256 || fileHeader.numInstruments >= MAX_INSTRUMENTS || fileHeader.numSamples >= MAX_SAMPLES - || !file.CanRead(256)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MT2FileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mtm.cpp =================================================================== --- soundlib/Load_mtm.cpp (revision 8929) +++ soundlib/Load_mtm.cpp (working copy) @@ -75,23 +75,65 @@ MPT_BINARY_STRUCT(MTMSampleHeader, 37) -bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "MTM", 3) + if(std::memcmp(fileHeader.id, "MTM", 3) || fileHeader.version >= 0x20 || fileHeader.lastOrder > 127 || fileHeader.beatsPerTrack > 64 || fileHeader.numChannels > 32 || fileHeader.numChannels == 0 - || !file.CanRead(sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_okt.cpp =================================================================== --- soundlib/Load_okt.cpp (revision 8929) +++ soundlib/Load_okt.cpp (working copy) @@ -258,6 +258,35 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(8)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("OKTASONG")) + { + return ProbeFailure; + } + OktIffChunk iffHead; + if(!file.ReadStruct(iffHead)) + { + return ProbeWantMoreData; + } + if(iffHead.chunksize == 0) + { + return ProbeFailure; + } + if((iffHead.signature & 0x7f7f7f7fu) != iffHead.signature) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { Index: soundlib/Load_plm.cpp =================================================================== --- soundlib/Load_plm.cpp (revision 8929) +++ soundlib/Load_plm.cpp (working copy) @@ -83,6 +83,44 @@ MPT_BINARY_STRUCT(PLMOrderItem, 4) +static bool ValidateHeader(const PLMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "PLM\x1A", 4) + || fileHeader.version != 0x10 + || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 + || fileHeader.headerSize < sizeof(PLMFileHeader) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PLMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -89,19 +127,28 @@ file.Rewind(); PLMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PLM\x1A", 4) - || fileHeader.version != 0x10 - || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 - || !file.Seek(fileHeader.headerSize) - || !file.CanRead(4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples))) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + if(!file.Seek(fileHeader.headerSize)) + { + return false; + } + InitializeGlobals(MOD_TYPE_PLM); InitializeChannels(); m_SongFlags = SONG_ITOLDEFFECTS; Index: soundlib/Load_psm.cpp =================================================================== --- soundlib/Load_psm.cpp (revision 8929) +++ soundlib/Load_psm.cpp (working copy) @@ -208,10 +208,51 @@ offset = 0; } return ConvertStrTo<uint16>(&patternID[offset]); +} + +static bool ValidateHeader(const PSMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.formatID, "PSM ", 4) + || std::memcmp(fileHeader.fileInfoID, "FILE", 4)) + { + return false; + } + return true; } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PSMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + PSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(chunkHeader.length == 0) + { + return ProbeFailure; + } + if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -244,8 +285,7 @@ #endif // MPT_PSM_DECRYPT // Check header - if(memcmp(fileHeader.formatID, "PSM ", 4) - || memcmp(fileHeader.fileInfoID, "FILE", 4)) + if(!ValidateHeader(fileHeader)) { return false; } @@ -1029,15 +1069,10 @@ MPT_BINARY_STRUCT(PSM16PatternHeader, 4) -bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) -//--------------------------------------------------------------------- +static bool ValidateHeader(const PSM16FileHeader &fileHeader) +//----------------------------------------------------------- { - file.Rewind(); - - // Is it a valid PSM16 file? - PSM16FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.formatID, "PSM\xFE", 4) + if(std::memcmp(fileHeader.formatID, "PSM\xFE", 4) || fileHeader.lineEnd != 0x1A || (fileHeader.formatVersion != 0x10 && fileHeader.formatVersion != 0x01) // why is this sometimes 0x01? || fileHeader.patternVersion != 0 // 255ch pattern version not supported (did anyone use this?) @@ -1047,8 +1082,45 @@ || std::max(fileHeader.numChannelsPlay, fileHeader.numChannelsReal) == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) +//--------------------------------------------------------------------- +{ + file.Rewind(); + + // Is it a valid PSM16 file? + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ptm.cpp =================================================================== --- soundlib/Load_ptm.cpp (revision 8929) +++ soundlib/Load_ptm.cpp (working copy) @@ -104,14 +104,10 @@ MPT_BINARY_STRUCT(PTMSampleHeader, 80) -bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const PTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - - PTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PTMF", 4) + if(std::memcmp(fileHeader.magic, "PTMF", 4) || fileHeader.dosEOF != 26 || fileHeader.versionHi > 2 || fileHeader.flags != 0 @@ -120,11 +116,57 @@ || !fileHeader.numOrders || fileHeader.numOrders > 256 || !fileHeader.numSamples || fileHeader.numSamples > 255 || !fileHeader.numPatterns || fileHeader.numPatterns > 128 - || !file.CanRead(fileHeader.numSamples * sizeof(PTMSampleHeader))) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.numSamples * sizeof(PTMSampleHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_s3m.cpp =================================================================== --- soundlib/Load_s3m.cpp (revision 8929) +++ soundlib/Load_s3m.cpp (working copy) @@ -172,6 +172,43 @@ }; +static bool ValidateHeader(const S3MFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "SCRM", 4) + || fileHeader.fileType != S3MFileHeader::idS3MType + || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + S3MFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -179,15 +216,20 @@ // Is it a valid S3M file? S3MFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || !file.CanRead(fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2) - || memcmp(fileHeader.magic, "SCRM", 4) - || fileHeader.fileType != S3MFileHeader::idS3MType - || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_sfx.cpp =================================================================== --- soundlib/Load_sfx.cpp (revision 8929) +++ soundlib/Load_sfx.cpp (working copy) @@ -96,6 +96,87 @@ return 0; } + +static bool ValidateHeader(const SFXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.numOrders > 128) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + SAMPLEINDEX numSamples = 0; + if(numSamples == 0) + { + file.Rewind(); + if(!file.CanRead(0x40)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x3c) && file.ReadMagic("SONG")) + { + numSamples = 15; + } + } + if(numSamples == 0) + { + file.Rewind(); + if(!file.CanRead(0x80)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x7c) && file.ReadMagic("SO31")) + { + numSamples = 31; + } + } + if(numSamples == 0) + { + return ProbeFailure; + } + file.Rewind(); + for(SAMPLEINDEX smp = 0; smp < numSamples; smp++) + { + if(file.ReadUint32BE() > 131072) + { + return ProbeFailure; + } + } + file.Skip(4); + if(!file.CanRead(2)) + { + return ProbeWantMoreData; + } + uint16 speed = file.ReadUint16BE(); + if(speed < 178) + { + return ProbeFailure; + } + if(!file.CanRead(sizeof(SFXSampleHeader) * numSamples)) + { + return ProbeWantMoreData; + } + file.Skip(sizeof(SFXSampleHeader) * numSamples); + SFXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -164,12 +245,18 @@ } SFXFileHeader fileHeader; - file.ReadStruct(fileHeader); - - if(fileHeader.numOrders > 128) + if(!file.ReadStruct(fileHeader)) + { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) Index: soundlib/Load_stm.cpp =================================================================== --- soundlib/Load_stm.cpp (revision 8929) +++ soundlib/Load_stm.cpp (working copy) @@ -94,6 +94,49 @@ MPT_BINARY_STRUCT(STMPatternData, 4*64*4) +static bool ValidateHeader(const STMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.filetype != 2 + || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. + || fileHeader.verMajor != 2 + || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 + || fileHeader.globalVolume > 64 + || (std::memcmp(fileHeader.trackername, "!Scream!", 8) + && std::memcmp(fileHeader.trackername, "BMOD2STM", 8) + && std::memcmp(fileHeader.trackername, "WUZAMOD!", 8)) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const STMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 31 * sizeof(STMSampleHeader) + 128; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -105,20 +148,20 @@ // After reviewing all STM files on ModLand and ModArchive, it was found that the // case-insensitive comparison is most likely not necessary for any files in the wild. STMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.filetype != 2 - || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. - || fileHeader.verMajor != 2 - || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 - || fileHeader.globalVolume > 64 - || (memcmp(fileHeader.trackername, "!Scream!", 8) - && memcmp(fileHeader.trackername, "BMOD2STM", 8) - && memcmp(fileHeader.trackername, "WUZAMOD!", 8)) - || !file.CanRead(31 * sizeof(STMSampleHeader) + 128)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_stp.cpp =================================================================== --- soundlib/Load_stp.cpp (revision 8929) +++ soundlib/Load_stp.cpp (working copy) @@ -23,6 +23,7 @@ // File header (except for "STP3" magic) struct STPFileHeader { + char magic[4]; uint16be version; uint8be numOrders; uint8be patternLength; @@ -38,7 +39,7 @@ uint16be sampleStructSize; }; -MPT_BINARY_STRUCT(STPFileHeader, 200); +MPT_BINARY_STRUCT(STPFileHeader, 204); // Sample header (common part between all versions) @@ -203,24 +204,57 @@ } -bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const STPFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - if(!file.ReadMagic("STP3")) - return false; - - STPFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) + if(std::memcmp(fileHeader.magic, "STP3", 4) || fileHeader.version > 2 || fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES || fileHeader.timerCount == 0 || fileHeader.midiCount != 50) + { return false; + } + return true; +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } InitializeGlobals(MOD_TYPE_STP); Index: soundlib/Load_ult.cpp =================================================================== --- soundlib/Load_ult.cpp (revision 8929) +++ soundlib/Load_ult.cpp (working copy) @@ -336,21 +336,53 @@ }; +static bool ValidateHeader(const UltFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.version < '1' + || fileHeader.version > '4' + || std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UltFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + UltFileHeader fileHeader; - - // Tracker ID - if(!file.ReadStruct(fileHeader) - || fileHeader.version < '1' - || fileHeader.version > '4' - || memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_xm.cpp =================================================================== --- soundlib/Load_xm.cpp (revision 8929) +++ soundlib/Load_xm.cpp (working copy) @@ -262,6 +262,43 @@ DECLARE_FLAGSET(TrackerVersions) +static bool ValidateHeader(const XMFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if(fileHeader.channels == 0 + || fileHeader.channels > MAX_BASECHANNELS + || std::memcmp(fileHeader.signature, "Extended Module: ", 17) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const XMFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + XMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -268,13 +305,17 @@ file.Rewind(); XMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.channels == 0 - || fileHeader.channels > MAX_BASECHANNELS - || memcmp(fileHeader.signature, "Extended Module: ", 17) - || !file.CanRead(fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments))) + if(!file.ReadStruct(fileHeader)) { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; } else if(loadFlags == onlyVerifyHeader) { return true; Index: soundlib/Sndfile.cpp =================================================================== --- soundlib/Sndfile.cpp (revision 8929) +++ soundlib/Sndfile.cpp (working copy) @@ -211,12 +211,141 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize) +//------------------------------------------------------------------------------------------------------------------------------------ +{ + const uint64 availableFileSize = file.GetLength(); + const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); + //const uint64 validFileSize = std::min<uint64>(fileSize, ProbeRecommendedSize); + const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; + //const uint64 goalMinimumSize = std::min<uint64>(goalSize, ProbeRecommendedSize); + if(pfilesize) + { + if(availableFileSize < std::min<uint64>(fileSize, ProbeRecommendedSize)) + { + if(availableFileSize < goalSize) + { + return ProbeWantMoreData; + } + } else + { + if(fileSize < goalSize) + { + return ProbeFailure; + } + } + return ProbeSuccess; + } +#if 0 + if(!pfilesize) + { + if(fileSize < goalSize && fileSize < ProbeRecommendedSize) + { + return ProbeWantMoreData; + } + return ProbeSuccess; + } +#else + return ProbeSuccess; +#endif +} + + const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; +#define MPT_DO_PROBE( storedResult , call ) \ + MPT_DO { \ + ProbeResult lastResult = call ; \ + if(lastResult == ProbeSuccess) { \ + return ProbeSuccess; \ + } else if(lastResult == ProbeWantMoreData) { \ + storedResult = ProbeWantMoreData; \ + } \ + } MPT_WHILE_0 \ +/**/ + + CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize) //------------------------------------------------------------------------------------------------------------------- { +#if 1 + ProbeResult result = ProbeFailure; + if(pfilesize && (*pfilesize < data.size())) + { + throw std::out_of_range(""); + } + if(!data.data()) + { + throw std::invalid_argument(""); + } + MemoryFileReader file(data); + if(flags & ProbeContainers) + { + MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize)); + } + if(flags & ProbeModules) + { + MPT_DO_PROBE(result, ProbeFileHeader669(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_Asylum(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_DSMI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDBM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDIGI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderFAR(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderGDM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderICE(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderITP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderJ2B(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderM15(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMDL(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMED(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMO3(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMOD(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMT2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderOKT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPLM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM16(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPT36(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderS3M(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSFX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderULT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXM(file, pfilesize)); + } + if(pfilesize) + { + if((result == ProbeWantMoreData) && (mpt::saturate_cast<std::size_t>(*pfilesize) <= data.size())) + { + // If the prober wants more data but we already reached EOF, + // probing must fail. + result = ProbeFailure; + } + } else + { + if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize)) + { + // If the prober wants more daat but we already provided the recommended required maximum, + // just return success as this is th ebest we can do for the suggestesd probing size. + result = ProbeSuccess; + } + } + return result; +#else uint64 filesize = 0; if(pfilesize) { @@ -257,6 +386,7 @@ } sndFile->Destroy(); return ProbeSuccess; +#endif } Index: soundlib/Sndfile.h =================================================================== --- soundlib/Sndfile.h (revision 8929) +++ soundlib/Sndfile.h (working copy) @@ -581,6 +581,8 @@ ProbeWantMoreData = -1 }; + static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); + static ProbeResult Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize); public: @@ -672,6 +674,49 @@ bool InitChannel(CHANNELINDEX nChn); void InitAmigaResampler(); + static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize); + + static ProbeResult ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize); + // Module Loaders bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -694,7 +739,6 @@ bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMed(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMod(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -710,9 +754,11 @@ bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + + bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector<const char *> GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored Index: soundlib/UMXTools.cpp =================================================================== --- soundlib/UMXTools.cpp (revision 8929) +++ soundlib/UMXTools.cpp (working copy) @@ -17,8 +17,9 @@ // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. -int32 ReadUMXIndex(FileReader &chunk) -//----------------------------------- +template <typename Tfile> +static int32 ReadUMXIndexImpl(Tfile &chunk) +//----------------------------------------- { enum { @@ -55,10 +56,17 @@ return result; } +int32 ReadUMXIndex(FileReader &chunk) +//----------------------------------- +{ + return ReadUMXIndexImpl(chunk); +} + // Returns true if the given nme exists in the name table. -bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) -//--------------------------------------------------------------------------------------------- +template <typename TFile> +static bool FindUMXNameTableEntryImpl(TFile &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------- { if(!name) { @@ -77,7 +85,7 @@ { if(fileHeader.packageVersion >= 64) { - int32 length = ReadUMXIndex(file); + int32 length = ReadUMXIndexImpl(file); if(length <= 0) { continue; @@ -110,7 +118,19 @@ return result; } +bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} + + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion) //------------------------------------------------------------------------- Index: soundlib/UMXTools.h =================================================================== --- soundlib/UMXTools.h (revision 8929) +++ soundlib/UMXTools.h (working copy) @@ -38,6 +38,9 @@ // Returns true if the given nme exists in the name table. bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name); +// Returns true if the given nme exists in the name table. +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name); + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion); Index: soundlib/load_j2b.cpp =================================================================== --- soundlib/load_j2b.cpp (revision 8929) +++ soundlib/load_j2b.cpp (working copy) @@ -615,6 +615,66 @@ } +struct AMFFRiffChunkFormat +{ + uint32le format; +}; + +MPT_BINARY_STRUCT(AMFFRiffChunkFormat, 4) + + +static bool ValidateHeader(const AMFFRiffChunk &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.id != AMFFRiffChunk::idRIFF) + { + return false; + } + if(fileHeader.GetLength() < 8 + sizeof(AMFFMainChunk)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const AMFFRiffChunkFormat &formatHeader) +//----------------------------------------------------------------- +{ + if(formatHeader.format != AMFFRiffChunk::idAMFF || formatHeader.format != AMFFRiffChunk::idAM__) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + AMFFRiffChunk fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(formatHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -624,15 +684,23 @@ { return false; } - - if(fileHeader.id != AMFFRiffChunk::idRIFF) + if(!ValidateHeader(fileHeader)) { return false; } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return false; + } + if(!ValidateHeader(formatHeader)) + { + return false; + } bool isAM; // false: AMFF, true: AM - uint32 format = file.ReadUint32LE(); + uint32 format = formatHeader.format; if(format == AMFFRiffChunk::idAMFF) isAM = false; // "AMFF" else if(format == AMFFRiffChunk::idAM__) @@ -877,6 +945,64 @@ return true; } + +static bool ValidateHeader(const J2BFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "MUSE", 4) + || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) + && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) + ) + { + return false; + } + if(fileHeader.packedLength == 0) + { + return false; + } + if(fileHeader.fileLength != fileHeader.packedLength + sizeof(J2BFileHeader)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const J2BFileHeader &fileHeader, uint64 filesize) +//---------------------------------------------------------------------------------- +{ + if(filesize != fileHeader.fileLength) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + J2BFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(fileHeader, *pfilesize)) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -891,17 +1017,21 @@ file.Rewind(); J2BFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MUSE", 4) - || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) - && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) - || fileHeader.fileLength != file.GetLength() + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(fileHeader.fileLength != file.GetLength() || fileHeader.packedLength != file.BytesLeft() - || fileHeader.packedLength == 0 ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } probe-refactor-v19.patch (110,567 bytes)
Index: libopenmpt/libopenmpt_version.h =================================================================== --- libopenmpt/libopenmpt_version.h (revision 8932) +++ libopenmpt/libopenmpt_version.h (working copy) @@ -21,7 +21,7 @@ /*! \brief libopenmpt patch version number */ #define OPENMPT_API_VERSION_PATCH 0 /*! \brief libopenmpt pre-release tag */ -#define OPENMPT_API_VERSION_PREREL "-pre.7" +#define OPENMPT_API_VERSION_PREREL "-pre.8" /*! \brief libopenmpt pre-release flag */ #define OPENMPT_API_VERSION_IS_PREREL 1 Index: libopenmpt/libopenmpt_version.mk =================================================================== --- libopenmpt/libopenmpt_version.mk (revision 8932) +++ libopenmpt/libopenmpt_version.mk (working copy) @@ -1,7 +1,7 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=3 LIBOPENMPT_VERSION_PATCH=0 -LIBOPENMPT_VERSION_PREREL=-pre.7 +LIBOPENMPT_VERSION_PREREL=-pre.8 LIBOPENMPT_LTVER_CURRENT=1 LIBOPENMPT_LTVER_REVISION=0 Index: soundlib/ContainerMMCMP.cpp =================================================================== --- soundlib/ContainerMMCMP.cpp (revision 8932) +++ soundlib/ContainerMMCMP.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -144,6 +145,66 @@ } +static bool ValidateHeader(const MMCMPFILEHEADER &mfh) +//---------------------------------------------------- +{ + if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) + { + return false; + } + if(mfh.hdrsize != sizeof(MMCMPHEADER)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const MMCMPHEADER &mmh) +//------------------------------------------------ +{ + if(mmh.nblocks == 0) + { + return false; + } + if(mmh.filesize == 0) + { + return false; + } + if(mmh.filesize > 0x80000000) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + MMCMPFILEHEADER mfh; + if(!file.ReadStruct(mfh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mfh)) + { + return ProbeFailure; + } + MMCMPHEADER mmh; + if(!file.ReadStruct(mmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(mmh)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------- { @@ -151,14 +212,23 @@ containerItems.clear(); MMCMPFILEHEADER mfh; - if(!file.ReadStruct(mfh)) return false; - if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) return false; - if(mfh.hdrsize != sizeof(MMCMPHEADER)) return false; + if(!file.ReadStruct(mfh)) + { + return false; + } + if(!ValidateHeader(mfh)) + { + return false; + } MMCMPHEADER mmh; - if(!file.ReadStruct(mmh)) return false; - if(mmh.nblocks == 0) return false; - if(mmh.filesize == 0) return false; - if(mmh.filesize > 0x80000000) return false; + if(!file.ReadStruct(mmh)) + { + return false; + } + if(!ValidateHeader(mmh)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; Index: soundlib/ContainerPP20.cpp =================================================================== --- soundlib/ContainerPP20.cpp (revision 8932) +++ soundlib/ContainerPP20.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -115,6 +116,50 @@ } +struct PP20header +{ + char magic[4]; // "PP20" + uint8be efficiency[4]; +}; + +MPT_BINARY_STRUCT(PP20header, 8) + + +static bool ValidateHeader(const PP20header &hdr) +//----------------------------------------------- +{ + if(std::memcmp(hdr.magic, "PP20", 4) != 0) + { + return false; + } + if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15 + || hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15 + || hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15 + || hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PP20header hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackPP20(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //------------------------------------------------------------------------------------------------------------ { @@ -121,20 +166,25 @@ file.Rewind(); containerItems.clear(); - if(!file.ReadMagic("PP20")) return false; - if(!file.CanRead(8)) return false; - uint8 efficiency[4]; - file.ReadArray(efficiency); - if(efficiency[0] < 9 || efficiency[0] > 15 - || efficiency[1] < 9 || efficiency[1] > 15 - || efficiency[2] < 9 || efficiency[2] > 15 - || efficiency[3] < 9 || efficiency[3] > 15) + PP20header hdr; + if(!file.ReadStruct(hdr)) + { return false; + } + if(!ValidateHeader(hdr)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } + if(!file.CanRead(4)) + { + return false; + } + containerItems.emplace_back(); containerItems.back().data_cache = mpt::make_unique<std::vector<char> >(); std::vector<char> & unpackedData = *(containerItems.back().data_cache); Index: soundlib/ContainerUMX.cpp =================================================================== --- soundlib/ContainerUMX.cpp (revision 8932) +++ soundlib/ContainerUMX.cpp (working copy) @@ -12,11 +12,48 @@ #include "Loaders.h" #include "UMXTools.h" #include "Container.h" +#include "Sndfile.h" OPENMPT_NAMESPACE_BEGIN +static bool ValidateHeader(const UMXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) + || fileHeader.nameCount == 0 + || fileHeader.exportCount == 0 + || fileHeader.importCount == 0 + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UMXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(!FindUMXNameTableEntryMemory(file, fileHeader, "music")) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -24,15 +61,14 @@ containerItems.clear(); UMXFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) - || fileHeader.nameCount == 0 - || fileHeader.exportCount == 0 - || fileHeader.importCount == 0 - ) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } // Note that this can be a false positive, e.g. Unreal maps will have music and sound // in their name table because they usually import such files. However, it spares us Index: soundlib/ContainerXPK.cpp =================================================================== --- soundlib/ContainerXPK.cpp (revision 8932) +++ soundlib/ContainerXPK.cpp (working copy) @@ -13,6 +13,7 @@ #include "../common/FileReader.h" #include "Container.h" +#include "Sndfile.h" #include <stdexcept> @@ -329,6 +330,68 @@ } +static bool ValidateHeader(const XPKFILEHEADER &header) +//----------------------------------------------------- +{ + if(std::memcmp(header.XPKF, "XPKF", 4) != 0) + { + return false; + } + if(std::memcmp(header.SQSH, "SQSH", 4) != 0) + { + return false; + } + if(header.SrcLen == 0) + { + return false; + } + if(header.DstLen == 0) + { + return false; + } + MPT_STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); + if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize) +//------------------------------------------------------------------------------ +{ + if(filesize < header.SrcLen - 8) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + XPKFILEHEADER header; + if(!file.ReadStruct(header)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(header)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(header, *pfilesize)) + { + return ProbeFailure; + } + } + return ProbeSuccess; +} + + bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags) //----------------------------------------------------------------------------------------------------------- { @@ -336,19 +399,24 @@ containerItems.clear(); XPKFILEHEADER header; - if(!file.ReadStruct(header)) return false; - if(std::memcmp(header.XPKF, "XPKF", 4) != 0) return false; - if(std::memcmp(header.SQSH, "SQSH", 4) != 0) return false; - if(header.SrcLen == 0) return false; - if(header.DstLen == 0) return false; - STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); - if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) return false; + if(!file.ReadStruct(header)) + { + return false; + } + if(!ValidateHeader(header)) + { + return false; + } if(loadFlags == ContainerOnlyVerifyHeader) { return true; } - if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) return false; + if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) + { + return false; + } + containerItems.emplace_back(); containerItems.back().data_cache = mpt::make_unique<std::vector<char> >(); std::vector<char> & unpackedData = *(containerItems.back().data_cache); Index: soundlib/Load_669.cpp =================================================================== --- soundlib/Load_669.cpp (revision 8932) +++ soundlib/Load_669.cpp (working copy) @@ -62,14 +62,9 @@ MPT_BINARY_STRUCT(_669Sample, 25) -bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const _669FileHeader &fileHeader) { - _669FileHeader fileHeader; - - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.magic, "if", 2) && memcmp(fileHeader.magic, "JN", 2)) + if((std::memcmp(fileHeader.magic, "if", 2) && std::memcmp(fileHeader.magic, "JN", 2)) || fileHeader.samples > 64 || fileHeader.restartPos >= 128 || fileHeader.patterns > 128) @@ -76,21 +71,68 @@ { return false; } - - for(size_t i = 0; i < CountOf(fileHeader.breaks); i++) + for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++) { if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE) + { return false; + } if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0) + { return false; + } if(fileHeader.breaks[i] >= 64) + { return false; + } } + return true; +} + +static uint64 GetHeaderMinimumAdditionalSize(const _669FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + _669FileHeader fileHeader; + + file.Rewind(); + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) { return true; - } else if(!file.CanRead(fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u)) + } + + if(!file.CanRead(GetHeaderMinimumAdditionalSize(fileHeader))) { return false; } Index: soundlib/Load_amf.cpp =================================================================== --- soundlib/Load_amf.cpp (revision 8932) +++ soundlib/Load_amf.cpp (working copy) @@ -80,6 +80,42 @@ MPT_BINARY_STRUCT(AMFFileHeader, 41) +static bool ValidateHeader(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------ +{ + if(std::memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) + || fileHeader.numSamples > 64 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AsylumFileHeader &fileHeader) +//------------------------------------------------------------------------------ +{ + return 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------------- +{ + AsylumFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------------- { @@ -86,14 +122,20 @@ file.Rewind(); AsylumFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25) - || fileHeader.numSamples > 64 - || !file.CanRead(256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -341,6 +383,37 @@ } +static bool ValidateHeader(const AMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.amf, "AMF", 3) + || fileHeader.version < 8 || fileHeader.version > 14 + || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------------- +{ + AMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------ { @@ -347,14 +420,16 @@ file.Rewind(); AMFFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.amf, "AMF", 3) - || fileHeader.version < 8 || fileHeader.version > 14 - || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ams.cpp =================================================================== --- soundlib/Load_ams.cpp (revision 8932) +++ soundlib/Load_ams.cpp (working copy) @@ -332,21 +332,76 @@ MPT_BINARY_STRUCT(AMSSampleHeader, 17) +static bool ValidateHeader(const AMSFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.versionHigh != 0x01) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("Extreme")) + { + return ProbeFailure; + } + AMSFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + if(!file.ReadMagic("Extreme")) + { + return false; + } AMSFileHeader fileHeader; - if(!file.ReadMagic("Extreme") - || !file.ReadStruct(fileHeader) - || !file.Skip(fileHeader.extraSize) - || !file.CanRead(3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u) - || fileHeader.versionHigh != 0x01) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(!file.Skip(fileHeader.extraSize)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } @@ -641,30 +696,93 @@ MPT_BINARY_STRUCT(AMS2Description, 11) +static bool ValidateHeader(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------- +{ + if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader) +//---------------------------------------------------------------------------- +{ + return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(7)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("AMShdr\x1A")) + { + return ProbeFailure; + } + if(!file.CanRead(1)) + { + return ProbeWantMoreData; + } + const uint8 songNameLength = file.ReadUint8(); + if(!file.Skip(songNameLength)) + { + return ProbeWantMoreData; + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { file.Rewind(); - AMS2FileHeader fileHeader; if(!file.ReadMagic("AMShdr\x1A")) { return false; } - - InitializeGlobals(MOD_TYPE_AMS2); - - if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName) - || !file.ReadStruct(fileHeader) - || fileHeader.versionHigh != 2 || fileHeader.versionLow > 2 - || !file.CanRead(36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u)) + std::string songName; + if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(songName)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + AMS2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + InitializeGlobals(MOD_TYPE_AMS2); + + m_songName = songName; + m_nInstruments = fileHeader.numIns; m_nChannels = 32; SetupMODPanning(true); Index: soundlib/Load_dbm.cpp =================================================================== --- soundlib/Load_dbm.cpp (revision 8932) +++ soundlib/Load_dbm.cpp (working copy) @@ -293,19 +293,51 @@ } +static bool ValidateHeader(const DBMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.dbm0, "DBM0", 4) + || fileHeader.trkVerHi > 3) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { - DBMFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.dbm0, "DBM0", 4) - || fileHeader.trkVerHi > 3) + DBMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_digi.cpp =================================================================== --- soundlib/Load_digi.cpp (revision 8932) +++ soundlib/Load_digi.cpp (working copy) @@ -73,6 +73,36 @@ } +static bool ValidateHeader(const DIGIFileHeader &fileHeader) +{ + if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20) + || !fileHeader.numChannels + || fileHeader.numChannels > 8 + || fileHeader.lastOrdIndex > 127) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + DIGIFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) //-------------------------------------------------------------------- { @@ -79,15 +109,16 @@ file.Rewind(); DIGIFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DIGI Booster module\0", 20) - || !fileHeader.numChannels - || fileHeader.numChannels > 8 - || fileHeader.lastOrdIndex > 127) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dmf.cpp =================================================================== --- soundlib/Load_dmf.cpp (revision 8932) +++ soundlib/Load_dmf.cpp (working copy) @@ -887,18 +887,51 @@ } +static bool ValidateHeader(const DMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "DDMF", 4) + || !fileHeader.version || fileHeader.version > 10) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { + file.Rewind(); + DMFFileHeader fileHeader; - file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "DDMF", 4) - || !fileHeader.version || fileHeader.version > 10) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_dsm.cpp =================================================================== --- soundlib/Load_dsm.cpp (revision 8932) +++ soundlib/Load_dsm.cpp (working copy) @@ -100,40 +100,100 @@ MPT_BINARY_STRUCT(DSMSampleHeader, 64) -bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct DSMHeader { - file.Rewind(); + char fileMagic0[4]; + char fileMagic1[4]; + char fileMagic2[4]; +}; - char fileMagic0[4], fileMagic1[4], fileMagic2[4]; - if(!file.ReadArray(fileMagic0)) return false; - if(!file.ReadArray(fileMagic1)) return false; - if(!file.ReadArray(fileMagic2)) return false; +MPT_BINARY_STRUCT(DSMHeader, 12) - if(!memcmp(fileMagic0, "RIFF", 4) - && !memcmp(fileMagic2, "DSMF", 4)) + +static bool ValidateHeader(const DSMHeader &fileHeader) +//----------------------------------------------------- +{ + if(!std::memcmp(fileHeader.fileMagic0, "RIFF", 4) + && !std::memcmp(fileHeader.fileMagic2, "DSMF", 4)) { // "Normal" DSM files with RIFF header // <RIFF> <file size> <DSMF> - } else if(!memcmp(fileMagic0, "DSMF", 4)) + return true; + } else if(!std::memcmp(fileHeader.fileMagic0, "DSMF", 4)) { // DSM files with alternative header // <DSMF> <4 bytes, usually 4x NUL or RIFF> <file size> <4 bytes, usually DSMF but not always> - file.Skip(4); + return true; } else { return false; } +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + if(!file.Skip(4)) + { + return ProbeWantMoreData; + } + } DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(std::memcmp(chunkHeader.magic, "SONG", 4)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} - file.ReadStruct(chunkHeader); + +bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + DSMHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0) + { + file.Skip(4); + } + DSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return false; + } // Technically, the song chunk could be anywhere in the file, but we're going to simplify // things by not using a chunk header here and just expect it to be right at the beginning. - if(memcmp(chunkHeader.magic, "SONG", 4)) + if(std::memcmp(chunkHeader.magic, "SONG", 4)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_dtm.cpp =================================================================== --- soundlib/Load_dtm.cpp (revision 8932) +++ soundlib/Load_dtm.cpp (working copy) @@ -181,6 +181,37 @@ MPT_BINARY_STRUCT(DTMText, 12) +static bool ValidateHeader(const DTMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "D.T.", 4) + || fileHeader.headerSize < sizeof(fileHeader) - 8u + || fileHeader.headerSize > 256 // Excessively long song title? + || fileHeader.type != 0) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + DTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -187,15 +218,16 @@ file.Rewind(); DTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "D.T.", 4) - || fileHeader.headerSize < sizeof(fileHeader) - 8u - || fileHeader.headerSize > 256 // Excessively long song title? - || fileHeader.type != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_far.cpp =================================================================== --- soundlib/Load_far.cpp (revision 8932) +++ soundlib/Load_far.cpp (working copy) @@ -102,6 +102,46 @@ MPT_BINARY_STRUCT(FARSampleHeader, 48) +static bool ValidateHeader(const FARFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 + || std::memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) + ) + { + return false; + } + if(fileHeader.headerLength < sizeof(FARFileHeader)) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const FARFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerLength - sizeof(FARFileHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + FARFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -108,14 +148,20 @@ file.Rewind(); FARFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "FAR\xFE", 4) != 0 - || memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3) - || !file.LengthIsAtLeast(fileHeader.headerLength)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_gdm.cpp =================================================================== --- soundlib/Load_gdm.cpp (revision 8932) +++ soundlib/Load_gdm.cpp (working copy) @@ -90,28 +90,61 @@ MPT_BINARY_STRUCT(GDMSampleHeader, 62) -bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static const MODTYPE gdmFormatOrigin[] = { - file.Rewind(); + MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM +}; - const MODTYPE gdmFormatOrigin[] = - { - MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM - }; - GDMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "GDM\xFE", 4) +static bool ValidateHeader(const GDMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "GDM\xFE", 4) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 - || memcmp(fileHeader.magic2, "GMFS", 4) + || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 - || fileHeader.originalFormat >= CountOf(gdmFormatOrigin) + || fileHeader.originalFormat >= mpt::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + GDMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_imf.cpp =================================================================== --- soundlib/Load_imf.cpp (revision 8932) +++ soundlib/Load_imf.cpp (working copy) @@ -351,19 +351,84 @@ } } + +static bool ValidateHeader(const IMFFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.im10, "IM10", 4) + || fileHeader.ordNum > 256 + || fileHeader.insNum >= MAX_INSTRUMENTS + ) + { + return false; + } + bool channelFound = false; + for(uint8 chn = 0; chn < 32; chn++) + { + switch(fileHeader.channels[chn].status) + { + case 0: // enabled; don't worry about it + channelFound = true; + break; + case 1: // mute + channelFound = true; + break; + case 2: // disabled + // nothing + break; + default: // uhhhh.... freak out + return false; + } + } + if(!channelFound) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + IMFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { IMFFileHeader fileHeader; file.Rewind(); - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.im10, "IM10", 4) - || fileHeader.ordNum > 256 - || fileHeader.insNum >= MAX_INSTRUMENTS - || !file.CanRead(256)) + if(!file.ReadStruct(fileHeader)) { return false; } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } // Read channel configuration std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled @@ -397,7 +462,9 @@ if(!detectedChannels) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + + if(loadFlags == onlyVerifyHeader) { return true; } Index: soundlib/Load_it.cpp =================================================================== --- soundlib/Load_it.cpp (revision 8932) +++ soundlib/Load_it.cpp (working copy) @@ -402,6 +402,43 @@ } +static bool ValidateHeader(const ITFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4)) + || fileHeader.insnum > 0xFF + || fileHeader.smpnum >= MAX_SAMPLES + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + ITFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -408,15 +445,20 @@ file.Rewind(); ITFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || (memcmp(fileHeader.id, "IMPM", 4) && memcmp(fileHeader.id, "tpm.", 4)) - || fileHeader.insnum > 0xFF - || fileHeader.smpnum >= MAX_SAMPLES - || !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_itp.cpp =================================================================== --- soundlib/Load_itp.cpp (revision 8932) +++ soundlib/Load_itp.cpp (working copy) @@ -60,6 +60,54 @@ MPT_BINARY_STRUCT(ITPModCommand, 6) +struct ITPHeader +{ + uint32le magic; + uint32le version; +}; + +MPT_BINARY_STRUCT(ITPHeader, 8) + + +static bool ValidateHeader(const ITPHeader &hdr) +//---------------------------------------------- +{ + if(hdr.magic != MAGIC4BE('.','i','t','p')) + { + return false; + } + if(hdr.version > 0x00000103 || hdr.version < 0x00000100) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr) +//---------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(hdr); + return 12 + 4 + 24 + 4 - sizeof(ITPHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + ITPHeader hdr; + if(!file.ReadStruct(hdr)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(hdr)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr)); +} + + bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------------- { @@ -81,22 +129,29 @@ ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file }; - uint32 version, size; - file.Rewind(); - // Check file ID - if(!file.CanRead(12 + 4 + 24 + 4) - || file.ReadUint32LE() != MAGIC4BE('.','i','t','p') // Magic bytes - || (version = file.ReadUint32LE()) > 0x00000103 // Format version - || version < 0x00000100) + ITPHeader hdr; + if(!file.ReadStruct(hdr)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(hdr)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(hdr)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + uint32 version, size; + version = hdr.version; + InitializeGlobals(MOD_TYPE_IT); m_playBehaviour.reset(); file.ReadSizedString<uint32le, mpt::String::maybeNullTerminated>(m_songName); Index: soundlib/Load_mdl.cpp =================================================================== --- soundlib/Load_mdl.cpp (revision 8932) +++ soundlib/Load_mdl.cpp (working copy) @@ -422,18 +422,50 @@ } +static bool ValidateHeader(const MDLFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.id, "DMDL", 4) + || fileHeader.version >= 0x20) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MDLFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); MDLFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "DMDL", 4) - || fileHeader.version >= 0x20) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_med.cpp =================================================================== --- soundlib/Load_med.cpp (revision 8932) +++ soundlib/Load_med.cpp (working copy) @@ -493,22 +493,68 @@ } +static bool ValidateHeader(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------- +{ + if(std::memcmp(pmmh.id, "MMD", 3) + || pmmh.id[3] < '0' || pmmh.id[3] > '3' + || pmmh.song == 0 + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh) +//----------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(pmmh); + return sizeof(MMD0SONGHEADER); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MEDMODULEHEADER pmmh; + if(!file.ReadStruct(pmmh)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(pmmh)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh)); +} + + bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { file.Rewind(); MEDMODULEHEADER pmmh; - uint32 dwSong; - if(!file.CanRead(512) - || !file.ReadStruct(pmmh) - || memcmp(pmmh.id, "MMD", 3) - || pmmh.id[3] < '0' || pmmh.id[3] > '3' - || (dwSong = pmmh.song) == 0 - || !file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + if(!file.ReadStruct(pmmh)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(pmmh)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(pmmh)))) + { + return false; + } + const uint32 dwSong = pmmh.song; + if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mo3.cpp =================================================================== --- soundlib/Load_mo3.cpp (revision 8932) +++ soundlib/Load_mo3.cpp (working copy) @@ -691,26 +691,74 @@ #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +struct MO3ContainerHeader +{ + char magic[3]; // MO3 + uint8le version; + uint32le musicSize; +}; +MPT_BINARY_STRUCT(MO3ContainerHeader, 8) + + +static bool ValidateHeader(const MO3ContainerHeader &containerHeader) +//------------------------------------------------------------------- +{ + if(std::memcmp(containerHeader.magic, "MO3", 3)) + { + return false; + } + if(containerHeader.musicSize <= sizeof(MO3FileHeader)) + { + return false; + } + if(containerHeader.version > 5) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(containerHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); - if(!file.CanRead(12) || !file.ReadMagic("MO3")) + MO3ContainerHeader containerHeader; + if(!file.ReadStruct(containerHeader)) { return false; } - const uint8 version = file.ReadUint8(); - const uint32 musicSize = file.ReadUint32LE(); - if(musicSize <= sizeof(MO3FileHeader) || version > 5) + if(!ValidateHeader(containerHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } + const uint8 version = containerHeader.version; + const uint32 musicSize = containerHeader.musicSize; + uint32 compressedSize = uint32_max; if(version >= 5) { Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 8932) +++ soundlib/Load_mod.cpp (working copy) @@ -654,20 +654,20 @@ } -bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +struct MODMagicResult { - char magic[4]; - if(!file.Seek(1080) || !file.ReadArray(magic)) - { - return false; - } + CHANNELINDEX m_nChannels = 4; + mpt::ustring m_madeWithTracker = mpt::ustring(); + bool isNoiseTracker = false; + bool isStartrekker = false; + bool isGenericMultiChannel = false; + bool setMODVBlankTiming = false; +}; - InitializeGlobals(MOD_TYPE_MOD); - m_nChannels = 4; - bool isNoiseTracker = false, isStartrekker = false, isGenericMultiChannel = false; - // Check MOD Magic +static bool CheckMODMagic(const char magic[4], MODMagicResult *result) +{ + MODMagicResult *r = result; if(IsMagic(magic, "M.K.") // ProTracker and compatible || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) || IsMagic(magic, "PATT") // ProTracker 3.6 @@ -674,66 +674,143 @@ || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible"); + } } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk || IsMagic(magic, "N.T.")) { - m_nChannels = 4; - m_madeWithTracker = MPT_USTRING("NoiseTracker"); - isNoiseTracker = true; + if(r) + { + r->m_nChannels = 4; + r->m_madeWithTracker = MPT_USTRING("NoiseTracker"); + r->isNoiseTracker = true; + } } else if(IsMagic(magic, "OKTA") || IsMagic(magic, "OCTA")) { // Oktalyzer - m_nChannels = 8; - m_madeWithTracker = MPT_USTRING("Oktalyzer"); + if(r) + { + r->m_nChannels = 8; + r->m_madeWithTracker = MPT_USTRING("Oktalyzer"); + } } else if(IsMagic(magic, "CD81") || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon - m_nChannels = magic[2] - '0'; - m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + if(r) + { + r->m_nChannels = magic[2] - '0'; + r->m_madeWithTracker = MPT_USTRING("Octalyser (Atari)"); + } } else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8') { // Digital Tracker on Atari Falcon - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Digital Tracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Digital Tracker"); + } } else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9') { // FLTx / EXOx - Startrekker by Exolon / Fairlight - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("Startrekker"); - isStartrekker = true; - m_playBehaviour.set(kMODVBlankTiming); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("Startrekker"); + r->isStartrekker = true; + r->setMODVBlankTiming = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3)) { // xCHN - Many trackers - m_nChannels = magic[0] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = magic[0] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9' && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers - m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; - m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); - isGenericMultiChannel = true; + if(r) + { + r->m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0'; + r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker"); + r->isGenericMultiChannel = true; + } } else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '4' && magic[3] <= '9') { // TDZx - TakeTracker - m_nChannels = magic[3] - '0'; - m_madeWithTracker = MPT_USTRING("TakeTracker"); + if(r) + { + r->m_nChannels = magic[3] - '0'; + r->m_madeWithTracker = MPT_USTRING("TakeTracker"); + } } else { return false; } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1080 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1080); + char magic[4]; + file.ReadArray(magic); + if(!CheckMODMagic(magic, nullptr)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + char magic[4]; + if(!file.Seek(1080) || !file.ReadArray(magic)) + { + return false; + } + + InitializeGlobals(MOD_TYPE_MOD); + + MODMagicResult modMagicResult; + if(!CheckMODMagic(magic, &modMagicResult)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) { return true; } + m_nChannels = modMagicResult.m_nChannels; + m_madeWithTracker = modMagicResult.m_madeWithTracker; + bool isNoiseTracker = modMagicResult.isNoiseTracker; + bool isStartrekker = modMagicResult.isStartrekker; + bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel; + if(modMagicResult.setMODVBlankTiming) + { + m_playBehaviour.set(kMODVBlankTiming); + } + LimitMax(m_nChannels, MAX_BASECHANNELS); // Startrekker 8 channel mod (needs special treatment, see below) @@ -1166,8 +1243,8 @@ // Check if a name string is valid (i.e. doesn't contain binary garbage data) template<size_t N> -static uint32 CountInvalidChars(char (&name)[N]) -//---------------------------------------------- +static uint32 CountInvalidChars(const char (&name)[N]) +//---------------------------------------------------- { uint32 invalidChars = 0; for(auto c : name) @@ -1195,25 +1272,127 @@ }; -bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- + +struct M15FileHeaders { - file.Rewind(); + char songname[20]; + MODSampleHeader sampleHeaders[15]; + MODFileHeader fileHeader; +}; - char songname[20]; - file.ReadArray(songname); +MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) + +static bool ValidateHeader(const M15FileHeaders &fileHeaders) +{ // In theory, sample and song names should only ever contain printable ASCII chars and null. // However, there are quite a few SoundTracker modules in the wild with random // characters. To still be able to distguish them from other formats, we just reject // files with *too* many bogus characters. Arbitrary threshold: 20 bogus characters in total // or more than 5 invalid characters just in the title alone. - uint32 invalidChars = CountInvalidChars(songname); - if(invalidChars > 5 || !file.CanRead(sizeof(MODSampleHeader) * 15 + sizeof(MODFileHeader))) + uint32 invalidChars = CountInvalidChars(fileHeaders.songname); + if(invalidChars > 5) { return false; } + SmpLength totalSampleLen = 0; + uint8 allVolumes = 0; + + for(SAMPLEINDEX smp = 0; smp < 15; smp++) + { + const MODSampleHeader &sampleHeader = fileHeaders.sampleHeaders[smp]; + + invalidChars += CountInvalidChars(sampleHeader.name); + + // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) + if(invalidChars > 48 + || sampleHeader.volume > 64 + || sampleHeader.finetune != 0 + || sampleHeader.length > 32768) + { + return false; + } + + totalSampleLen += sampleHeader.length; + allVolumes |= sampleHeader.volume; + + } + + // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) + if(totalSampleLen == 0 || allVolumes == 0) + { + return false; + } + + // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. + // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. + if(fileHeaders.fileHeader.numOrders > 128 || fileHeaders.fileHeader.restartPos > 220) + { + return false; + } + + for(uint8 ord : fileHeaders.fileHeader.orderList) + { + // Sanity check: 64 patterns max. + if(ord > 63) + { + return false; + } + } + + bool allPatternsNull = true; + for(uint8 pat : fileHeaders.fileHeader.orderList) + { + if(pat != 0 && pat < 128) + { + allPatternsNull = false; + } + } + if(fileHeaders.fileHeader.restartPos == 0 && fileHeaders.fileHeader.numOrders == 0 && allPatternsNull) + { + return false; + } + + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeaders)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + M15FileHeaders fileHeaders; + if(!file.ReadStruct(fileHeaders)) + { + return false; + } + if(!ValidateHeader(fileHeaders)) + { + return false; + } + + char songname[20]; + std::memcpy(songname, fileHeaders.songname, 20); + InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); @@ -1224,26 +1403,15 @@ bool hasDiskNames = true; SmpLength totalSampleLen = 0; - uint8 allVolumes = 0; m_nSamples = 15; + file.Seek(20); for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { MODSampleHeader sampleHeader; ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true); - invalidChars += CountInvalidChars(sampleHeader.name); - // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) - if(invalidChars > 48 - || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) - { - return false; - } - totalSampleLen += Samples[smp].nLength; - allVolumes |= sampleHeader.volume; if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':')) { @@ -1264,36 +1432,22 @@ minVersion = std::max(minVersion, MST1_00); } - // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) - if(totalSampleLen == 0 || allVolumes == 0) - return false; - MODFileHeader fileHeader; file.ReadStruct(fileHeader); - // Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220]. - // There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit. - if(fileHeader.numOrders > 128 || fileHeader.restartPos > 220) - return false; + ReadOrderFromArray(Order(), fileHeader.orderList); + PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); - for(uint8 ord : fileHeader.orderList) + // Most likely just a file with lots of NULs at the start + if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) { - // Sanity check: 64 patterns max. - if(ord > 63) - return false; + return false; } - ReadOrderFromArray(Order(), fileHeader.orderList); - PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); - // Let's see if the file is too small (including some overhead for broken files like sll7.mod or ghostbus.mod) if(file.BytesLeft() + 65536 < numPatterns * 64u * 4u + totalSampleLen) return false; - // Most likely just a file with lots of NULs at the start - if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) - return false; - if(loadFlags == onlyVerifyHeader) return true; @@ -1579,6 +1733,55 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(1464 + 4)) + { + return ProbeWantMoreData; + } + file.Seek(1464); + char magic[4]; + file.ReadArray(magic); + if(!IsMagic(magic, "MTN\0") && !IsMagic(magic, "IT10")) + { + return ProbeFailure; + } + file.Seek(20); + uint32 invalidChars = 0; + for(SAMPLEINDEX smp = 1; smp <= 31; smp++) + { + MODSampleHeader sampleHeader; + if(!file.ReadStruct(sampleHeader)) + { + return ProbeWantMoreData; + } + invalidChars += CountInvalidChars(sampleHeader.name); + } + if(invalidChars > 256) + { + return ProbeFailure; + } + const uint8 numOrders = file.ReadUint8(); + const uint8 numTracks = file.ReadUint8(); + if(numOrders > 128) + { + return ProbeFailure; + } + uint8 tracks[128 * 4]; + file.ReadArray(tracks); + for(auto track : tracks) + { + if(track > numTracks) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // SoundTracker 2.6 / Ice Tracker variation of the MOD format // The only real difference to other SoundTracker formats is the way patterns are stored: // Every pattern consists of four independent, re-usable tracks. @@ -1597,11 +1800,15 @@ m_playBehaviour.set(kMODSampleSwap); // untested if(IsMagic(magic, "MTN\0")) + { m_madeWithTracker = MPT_USTRING("SoundTracker 2.6"); - else if(IsMagic(magic, "IT10")) + } else if(IsMagic(magic, "IT10")) + { m_madeWithTracker = MPT_USTRING("Ice Tracker 1.0 / 1.1"); - else + } else + { return false; + } // Reading song title file.Seek(0); @@ -1623,7 +1830,9 @@ const uint8 numOrders = file.ReadUint8(); const uint8 numTracks = file.ReadUint8(); if(numOrders > 128) + { return false; + } uint8 tracks[128 * 4]; file.ReadArray(tracks); @@ -1630,11 +1839,15 @@ for(auto track : tracks) { if(track > numTracks) + { return false; + } } if(loadFlags == onlyVerifyHeader) + { return true; + } // Now we can be pretty sure that this is a valid MOD file. Set up default song settings. m_nChannels = 4; @@ -1732,6 +1945,48 @@ +struct PT36Header +{ + char magicFORM[4]; // "FORM" + uint8be dummy1[4]; + char magicMODL[4]; // "MODL" +}; + +MPT_BINARY_STRUCT(PT36Header, 12); + + +static bool ValidateHeader(const PT36Header &fileHeader) +//------------------------------------------------------ +{ + if(std::memcmp(fileHeader.magicFORM, "FORM", 4)) + { + return false; + } + if(std::memcmp(fileHeader.magicMODL, "MODL", 4)) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize) +//----------------------------------------------------------------------------------------------------- +{ + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + // ProTracker 3.6 version of the MOD format // Basically just a normal ProTracker mod with different magic, wrapped in an IFF file. // The "PTDT" chunk is passed to the normal MOD loader. @@ -1739,16 +1994,20 @@ //-------------------------------------------------------------------- { file.Rewind(); - if(!file.ReadMagic("FORM") - || !file.Skip(4) - || !file.ReadMagic("MODL")) + + PT36Header fileHeader; + if(!file.ReadStruct(fileHeader)) { return false; } - + if(!ValidateHeader(fileHeader)) + { + return false; + } + bool ok = false, infoOk = false; FileReader commentChunk; - mpt::ustring version = MPT_USTRING("3.6"); + mpt::ustring version; PT36InfoChunk info; MemsetZero(info); @@ -1799,6 +2058,11 @@ break; } } while(file.ReadStruct(iffHead)); + + if(version.empty()) + { + version = MPT_USTRING("3.6"); + } // both an info chunk and a module are required if(ok && infoOk) Index: soundlib/Load_mt2.cpp =================================================================== --- soundlib/Load_mt2.cpp (revision 8932) +++ soundlib/Load_mt2.cpp (working copy) @@ -395,23 +395,66 @@ } -bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MT2FileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MT2FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MT20", 4) + if(std::memcmp(fileHeader.signature, "MT20", 4) || fileHeader.version < 0x200 || fileHeader.version >= 0x300 || fileHeader.numChannels < 1 || fileHeader.numChannels > 64 || fileHeader.numOrders > 256 || fileHeader.numInstruments >= MAX_INSTRUMENTS || fileHeader.numSamples >= MAX_SAMPLES - || !file.CanRead(256)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MT2FileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 256; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MT2FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_mtm.cpp =================================================================== --- soundlib/Load_mtm.cpp (revision 8932) +++ soundlib/Load_mtm.cpp (working copy) @@ -75,23 +75,65 @@ MPT_BINARY_STRUCT(MTMSampleHeader, 37) -bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const MTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - MTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.id, "MTM", 3) + if(std::memcmp(fileHeader.id, "MTM", 3) || fileHeader.version >= 0x20 || fileHeader.lastOrder > 127 || fileHeader.beatsPerTrack > 64 || fileHeader.numChannels > 32 || fileHeader.numChannels == 0 - || !file.CanRead(sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize)) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + MTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_okt.cpp =================================================================== --- soundlib/Load_okt.cpp (revision 8932) +++ soundlib/Load_okt.cpp (working copy) @@ -258,6 +258,35 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + if(!file.CanRead(8)) + { + return ProbeWantMoreData; + } + if(!file.ReadMagic("OKTASONG")) + { + return ProbeFailure; + } + OktIffChunk iffHead; + if(!file.ReadStruct(iffHead)) + { + return ProbeWantMoreData; + } + if(iffHead.chunksize == 0) + { + return ProbeFailure; + } + if((iffHead.signature & 0x7f7f7f7fu) != iffHead.signature) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { Index: soundlib/Load_plm.cpp =================================================================== --- soundlib/Load_plm.cpp (revision 8932) +++ soundlib/Load_plm.cpp (working copy) @@ -83,6 +83,44 @@ MPT_BINARY_STRUCT(PLMOrderItem, 4) +static bool ValidateHeader(const PLMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "PLM\x1A", 4) + || fileHeader.version != 0x10 + || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 + || fileHeader.headerSize < sizeof(PLMFileHeader) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PLMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -89,19 +127,28 @@ file.Rewind(); PLMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PLM\x1A", 4) - || fileHeader.version != 0x10 - || fileHeader.numChannels == 0 || fileHeader.numChannels > 32 - || !file.Seek(fileHeader.headerSize) - || !file.CanRead(4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples))) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } + if(!file.Seek(fileHeader.headerSize)) + { + return false; + } + InitializeGlobals(MOD_TYPE_PLM); InitializeChannels(); m_SongFlags = SONG_ITOLDEFFECTS; Index: soundlib/Load_psm.cpp =================================================================== --- soundlib/Load_psm.cpp (revision 8932) +++ soundlib/Load_psm.cpp (working copy) @@ -208,10 +208,51 @@ offset = 0; } return ConvertStrTo<uint16>(&patternID[offset]); +} + +static bool ValidateHeader(const PSMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.formatID, "PSM ", 4) + || std::memcmp(fileHeader.fileInfoID, "FILE", 4)) + { + return false; + } + return true; } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PSMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + PSMChunk chunkHeader; + if(!file.ReadStruct(chunkHeader)) + { + return ProbeWantMoreData; + } + if(chunkHeader.length == 0) + { + return ProbeFailure; + } + if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII? + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -244,8 +285,7 @@ #endif // MPT_PSM_DECRYPT // Check header - if(memcmp(fileHeader.formatID, "PSM ", 4) - || memcmp(fileHeader.fileInfoID, "FILE", 4)) + if(!ValidateHeader(fileHeader)) { return false; } @@ -1029,15 +1069,10 @@ MPT_BINARY_STRUCT(PSM16PatternHeader, 4) -bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) -//--------------------------------------------------------------------- +static bool ValidateHeader(const PSM16FileHeader &fileHeader) +//----------------------------------------------------------- { - file.Rewind(); - - // Is it a valid PSM16 file? - PSM16FileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.formatID, "PSM\xFE", 4) + if(std::memcmp(fileHeader.formatID, "PSM\xFE", 4) || fileHeader.lineEnd != 0x1A || (fileHeader.formatVersion != 0x10 && fileHeader.formatVersion != 0x01) // why is this sometimes 0x01? || fileHeader.patternVersion != 0 // 255ch pattern version not supported (did anyone use this?) @@ -1047,8 +1082,45 @@ || std::max(fileHeader.numChannelsPlay, fileHeader.numChannelsReal) == 0) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize) +//------------------------------------------------------------------------------------------------------ +{ + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) +//--------------------------------------------------------------------- +{ + file.Rewind(); + + // Is it a valid PSM16 file? + PSM16FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_ptm.cpp =================================================================== --- soundlib/Load_ptm.cpp (revision 8932) +++ soundlib/Load_ptm.cpp (working copy) @@ -104,14 +104,10 @@ MPT_BINARY_STRUCT(PTMSampleHeader, 80) -bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const PTMFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - - PTMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "PTMF", 4) + if(std::memcmp(fileHeader.magic, "PTMF", 4) || fileHeader.dosEOF != 26 || fileHeader.versionHi > 2 || fileHeader.flags != 0 @@ -120,11 +116,57 @@ || !fileHeader.numOrders || fileHeader.numOrders > 256 || !fileHeader.numSamples || fileHeader.numSamples > 255 || !fileHeader.numPatterns || fileHeader.numPatterns > 128 - || !file.CanRead(fileHeader.numSamples * sizeof(PTMSampleHeader))) + ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const PTMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.numSamples * sizeof(PTMSampleHeader); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + PTMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_s3m.cpp =================================================================== --- soundlib/Load_s3m.cpp (revision 8932) +++ soundlib/Load_s3m.cpp (working copy) @@ -172,6 +172,43 @@ }; +static bool ValidateHeader(const S3MFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.magic, "SCRM", 4) + || fileHeader.fileType != S3MFileHeader::idS3MType + || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + S3MFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -179,15 +216,20 @@ // Is it a valid S3M file? S3MFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || !file.CanRead(fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2) - || memcmp(fileHeader.magic, "SCRM", 4) - || fileHeader.fileType != S3MFileHeader::idS3MType - || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_sfx.cpp =================================================================== --- soundlib/Load_sfx.cpp (revision 8932) +++ soundlib/Load_sfx.cpp (working copy) @@ -96,6 +96,87 @@ return 0; } + +static bool ValidateHeader(const SFXFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.numOrders > 128) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + SAMPLEINDEX numSamples = 0; + if(numSamples == 0) + { + file.Rewind(); + if(!file.CanRead(0x40)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x3c) && file.ReadMagic("SONG")) + { + numSamples = 15; + } + } + if(numSamples == 0) + { + file.Rewind(); + if(!file.CanRead(0x80)) + { + return ProbeWantMoreData; + } + if(file.Seek(0x7c) && file.ReadMagic("SO31")) + { + numSamples = 31; + } + } + if(numSamples == 0) + { + return ProbeFailure; + } + file.Rewind(); + for(SAMPLEINDEX smp = 0; smp < numSamples; smp++) + { + if(file.ReadUint32BE() > 131072) + { + return ProbeFailure; + } + } + file.Skip(4); + if(!file.CanRead(2)) + { + return ProbeWantMoreData; + } + uint16 speed = file.ReadUint16BE(); + if(speed < 178) + { + return ProbeFailure; + } + if(!file.CanRead(sizeof(SFXSampleHeader) * numSamples)) + { + return ProbeWantMoreData; + } + file.Skip(sizeof(SFXSampleHeader) * numSamples); + SFXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -164,12 +245,18 @@ } SFXFileHeader fileHeader; - file.ReadStruct(fileHeader); - - if(fileHeader.numOrders > 128) + if(!file.ReadStruct(fileHeader)) + { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) Index: soundlib/Load_stm.cpp =================================================================== --- soundlib/Load_stm.cpp (revision 8932) +++ soundlib/Load_stm.cpp (working copy) @@ -94,6 +94,49 @@ MPT_BINARY_STRUCT(STMPatternData, 4*64*4) +static bool ValidateHeader(const STMFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.filetype != 2 + || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. + || fileHeader.verMajor != 2 + || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 + || fileHeader.globalVolume > 64 + || (std::memcmp(fileHeader.trackername, "!Scream!", 8) + && std::memcmp(fileHeader.trackername, "BMOD2STM", 8) + && std::memcmp(fileHeader.trackername, "WUZAMOD!", 8)) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const STMFileHeader &fileHeader) +//--------------------------------------------------------------------------- +{ + MPT_UNREFERENCED_PARAMETER(fileHeader); + return 31 * sizeof(STMSampleHeader) + 128; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -105,20 +148,20 @@ // After reviewing all STM files on ModLand and ModArchive, it was found that the // case-insensitive comparison is most likely not necessary for any files in the wild. STMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.filetype != 2 - || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2. - || fileHeader.verMajor != 2 - || fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21 - || fileHeader.globalVolume > 64 - || (memcmp(fileHeader.trackername, "!Scream!", 8) - && memcmp(fileHeader.trackername, "BMOD2STM", 8) - && memcmp(fileHeader.trackername, "WUZAMOD!", 8)) - || !file.CanRead(31 * sizeof(STMSampleHeader) + 128)) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_stp.cpp =================================================================== --- soundlib/Load_stp.cpp (revision 8932) +++ soundlib/Load_stp.cpp (working copy) @@ -23,6 +23,7 @@ // File header (except for "STP3" magic) struct STPFileHeader { + char magic[4]; uint16be version; uint8be numOrders; uint8be patternLength; @@ -38,7 +39,7 @@ uint16be sampleStructSize; }; -MPT_BINARY_STRUCT(STPFileHeader, 200); +MPT_BINARY_STRUCT(STPFileHeader, 204); // Sample header (common part between all versions) @@ -203,24 +204,57 @@ } -bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------- +static bool ValidateHeader(const STPFileHeader &fileHeader) +//--------------------------------------------------------- { - file.Rewind(); - if(!file.ReadMagic("STP3")) - return false; - - STPFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) + if(std::memcmp(fileHeader.magic, "STP3", 4) || fileHeader.version > 2 || fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES || fileHeader.timerCount == 0 || fileHeader.midiCount != 50) + { return false; + } + return true; +} + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) +//------------------------------------------------------------------- +{ + file.Rewind(); + + STPFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } if(loadFlags == onlyVerifyHeader) + { return true; + } InitializeGlobals(MOD_TYPE_STP); Index: soundlib/Load_ult.cpp =================================================================== --- soundlib/Load_ult.cpp (revision 8932) +++ soundlib/Load_ult.cpp (working copy) @@ -336,21 +336,53 @@ }; +static bool ValidateHeader(const UltFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.version < '1' + || fileHeader.version > '4' + || std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) + ) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + UltFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); + UltFileHeader fileHeader; - - // Tracker ID - if(!file.ReadStruct(fileHeader) - || fileHeader.version < '1' - || fileHeader.version > '4' - || memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) != 0) + if(!file.ReadStruct(fileHeader)) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(!ValidateHeader(fileHeader)) { + return false; + } + if(loadFlags == onlyVerifyHeader) + { return true; } Index: soundlib/Load_xm.cpp =================================================================== --- soundlib/Load_xm.cpp (revision 8932) +++ soundlib/Load_xm.cpp (working copy) @@ -262,6 +262,43 @@ DECLARE_FLAGSET(TrackerVersions) +static bool ValidateHeader(const XMFileHeader &fileHeader) +//-------------------------------------------------------- +{ + if(fileHeader.channels == 0 + || fileHeader.channels > MAX_BASECHANNELS + || std::memcmp(fileHeader.signature, "Extended Module: ", 17) + ) + { + return false; + } + return true; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const XMFileHeader &fileHeader) +//-------------------------------------------------------------------------- +{ + return fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments); +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + XMFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -268,13 +305,17 @@ file.Rewind(); XMFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || fileHeader.channels == 0 - || fileHeader.channels > MAX_BASECHANNELS - || memcmp(fileHeader.signature, "Extended Module: ", 17) - || !file.CanRead(fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments))) + if(!file.ReadStruct(fileHeader)) { return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; } else if(loadFlags == onlyVerifyHeader) { return true; Index: soundlib/Sndfile.cpp =================================================================== --- soundlib/Sndfile.cpp (revision 8932) +++ soundlib/Sndfile.cpp (working copy) @@ -211,12 +211,141 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize) +//------------------------------------------------------------------------------------------------------------------------------------ +{ + const uint64 availableFileSize = file.GetLength(); + const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); + //const uint64 validFileSize = std::min<uint64>(fileSize, ProbeRecommendedSize); + const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; + //const uint64 goalMinimumSize = std::min<uint64>(goalSize, ProbeRecommendedSize); + if(pfilesize) + { + if(availableFileSize < std::min<uint64>(fileSize, ProbeRecommendedSize)) + { + if(availableFileSize < goalSize) + { + return ProbeWantMoreData; + } + } else + { + if(fileSize < goalSize) + { + return ProbeFailure; + } + } + return ProbeSuccess; + } +#if 0 + if(!pfilesize) + { + if(fileSize < goalSize && fileSize < ProbeRecommendedSize) + { + return ProbeWantMoreData; + } + return ProbeSuccess; + } +#else + return ProbeSuccess; +#endif +} + + const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; +#define MPT_DO_PROBE( storedResult , call ) \ + MPT_DO { \ + ProbeResult lastResult = call ; \ + if(lastResult == ProbeSuccess) { \ + return ProbeSuccess; \ + } else if(lastResult == ProbeWantMoreData) { \ + storedResult = ProbeWantMoreData; \ + } \ + } MPT_WHILE_0 \ +/**/ + + CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize) //------------------------------------------------------------------------------------------------------------------- { +#if 1 + ProbeResult result = ProbeFailure; + if(pfilesize && (*pfilesize < data.size())) + { + throw std::out_of_range(""); + } + if(!data.data()) + { + throw std::invalid_argument(""); + } + MemoryFileReader file(data); + if(flags & ProbeContainers) + { + MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize)); + } + if(flags & ProbeModules) + { + MPT_DO_PROBE(result, ProbeFileHeader669(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_Asylum(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMF_DSMI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderAMS2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDBM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDIGI(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderDSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderFAR(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderGDM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderICE(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIMF(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderIT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderITP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderJ2B(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderM15(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMDL(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMED(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMO3(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMOD(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMT2(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderMTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderOKT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPLM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPSM16(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPT36(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderPTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderS3M(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSFX(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTM(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderSTP(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderULT(file, pfilesize)); + MPT_DO_PROBE(result, ProbeFileHeaderXM(file, pfilesize)); + } + if(pfilesize) + { + if((result == ProbeWantMoreData) && (mpt::saturate_cast<std::size_t>(*pfilesize) <= data.size())) + { + // If the prober wants more data but we already reached EOF, + // probing must fail. + result = ProbeFailure; + } + } else + { + if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize)) + { + // If the prober wants more daat but we already provided the recommended required maximum, + // just return success as this is th ebest we can do for the suggestesd probing size. + result = ProbeSuccess; + } + } + return result; +#else uint64 filesize = 0; if(pfilesize) { @@ -257,6 +386,7 @@ } sndFile->Destroy(); return ProbeSuccess; +#endif } Index: soundlib/Sndfile.h =================================================================== --- soundlib/Sndfile.h (revision 8932) +++ soundlib/Sndfile.h (working copy) @@ -581,6 +581,8 @@ ProbeWantMoreData = -1 }; + static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); + static ProbeResult Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize); public: @@ -672,6 +674,49 @@ bool InitChannel(CHANNELINDEX nChn); void InitAmigaResampler(); + static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize); + + static ProbeResult ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize); + // Module Loaders bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -694,7 +739,6 @@ bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMed(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMod(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -710,9 +754,11 @@ bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + + bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector<const char *> GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored Index: soundlib/UMXTools.cpp =================================================================== --- soundlib/UMXTools.cpp (revision 8932) +++ soundlib/UMXTools.cpp (working copy) @@ -17,8 +17,9 @@ // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. -int32 ReadUMXIndex(FileReader &chunk) -//----------------------------------- +template <typename Tfile> +static int32 ReadUMXIndexImpl(Tfile &chunk) +//----------------------------------------- { enum { @@ -55,10 +56,17 @@ return result; } +int32 ReadUMXIndex(FileReader &chunk) +//----------------------------------- +{ + return ReadUMXIndexImpl(chunk); +} + // Returns true if the given nme exists in the name table. -bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) -//--------------------------------------------------------------------------------------------- +template <typename TFile> +static bool FindUMXNameTableEntryImpl(TFile &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------- { if(!name) { @@ -77,7 +85,7 @@ { if(fileHeader.packageVersion >= 64) { - int32 length = ReadUMXIndex(file); + int32 length = ReadUMXIndexImpl(file); if(length <= 0) { continue; @@ -110,7 +118,19 @@ return result; } +bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name) +//--------------------------------------------------------------------------------------------------------- +{ + return FindUMXNameTableEntryImpl(file, fileHeader, name); +} + + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion) //------------------------------------------------------------------------- Index: soundlib/UMXTools.h =================================================================== --- soundlib/UMXTools.h (revision 8932) +++ soundlib/UMXTools.h (working copy) @@ -38,6 +38,9 @@ // Returns true if the given nme exists in the name table. bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name); +// Returns true if the given nme exists in the name table. +bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name); + // Read an entry from the name table. std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion); Index: soundlib/load_j2b.cpp =================================================================== --- soundlib/load_j2b.cpp (revision 8932) +++ soundlib/load_j2b.cpp (working copy) @@ -615,6 +615,66 @@ } +struct AMFFRiffChunkFormat +{ + uint32le format; +}; + +MPT_BINARY_STRUCT(AMFFRiffChunkFormat, 4) + + +static bool ValidateHeader(const AMFFRiffChunk &fileHeader) +//--------------------------------------------------------- +{ + if(fileHeader.id != AMFFRiffChunk::idRIFF) + { + return false; + } + if(fileHeader.GetLength() < 8 + sizeof(AMFFMainChunk)) + { + return false; + } + return true; +} + + +static bool ValidateHeader(const AMFFRiffChunkFormat &formatHeader) +//----------------------------------------------------------------- +{ + if(formatHeader.format != AMFFRiffChunk::idAMFF || formatHeader.format != AMFFRiffChunk::idAM__) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize) +//--------------------------------------------------------------------------------------------------- +{ + AMFFRiffChunk fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(formatHeader)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { @@ -624,15 +684,23 @@ { return false; } - - if(fileHeader.id != AMFFRiffChunk::idRIFF) + if(!ValidateHeader(fileHeader)) { return false; } + AMFFRiffChunkFormat formatHeader; + if(!file.ReadStruct(formatHeader)) + { + return false; + } + if(!ValidateHeader(formatHeader)) + { + return false; + } bool isAM; // false: AMFF, true: AM - uint32 format = file.ReadUint32LE(); + uint32 format = formatHeader.format; if(format == AMFFRiffChunk::idAMFF) isAM = false; // "AMFF" else if(format == AMFFRiffChunk::idAM__) @@ -877,6 +945,64 @@ return true; } + +static bool ValidateHeader(const J2BFileHeader &fileHeader) +//--------------------------------------------------------- +{ + if(std::memcmp(fileHeader.signature, "MUSE", 4) + || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) + && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) + ) + { + return false; + } + if(fileHeader.packedLength == 0) + { + return false; + } + if(fileHeader.fileLength != fileHeader.packedLength + sizeof(J2BFileHeader)) + { + return false; + } + return true; +} + + +static bool ValidateHeaderFileSize(const J2BFileHeader &fileHeader, uint64 filesize) +//---------------------------------------------------------------------------------- +{ + if(filesize != fileHeader.fileLength) + { + return false; + } + return true; +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize) +//---------------------------------------------------------------------------------------------------- +{ + J2BFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + if(pfilesize) + { + if(!ValidateHeaderFileSize(fileHeader, *pfilesize)) + { + return ProbeFailure; + } + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { @@ -891,17 +1017,21 @@ file.Rewind(); J2BFileHeader fileHeader; - if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.signature, "MUSE", 4) - || (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM) - && fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF) - || fileHeader.fileLength != file.GetLength() + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(fileHeader.fileLength != file.GetLength() || fileHeader.packedLength != file.BytesLeft() - || fileHeader.packedLength == 0 ) { return false; - } else if(loadFlags == onlyVerifyHeader) + } + if(loadFlags == onlyVerifyHeader) { return true; } | ||||
Has the bug occurred in previous versions? | |||||
Tested code revision (in case you know it) | |||||
Date Modified | Username | Field | Change |
---|---|---|---|
2017-01-24 10:39 | manx | New Issue | |
2017-01-24 10:39 | manx | Status | new => assigned |
2017-01-24 10:39 | manx | Assigned To | => manx |
2017-05-24 10:21 | manx | Target Version | OpenMPT 1.?? (libopenmpt 1.0) (goals) => OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first) |
2017-09-17 09:28 | manx | File Added: probe-refactor-v11.patch | |
2017-09-17 09:34 | manx | Relationship added | related to 0000575 |
2017-09-20 08:43 | manx | File Added: probe-refactor-v18.patch | |
2017-09-21 06:38 | manx | File Added: probe-refactor-v19.patch | |
2017-09-21 06:44 | manx | Status | assigned => resolved |
2017-09-21 06:44 | manx | Resolution | open => fixed |
2017-09-21 06:44 | manx | Fixed in Version | => OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first) |
2017-09-21 06:44 | manx | Note Added: 0003224 |