View Issue Details

IDProjectCategoryView StatusLast Update
0000914OpenMPT[All Projects] libopenmptpublic2017-09-21 06:44
ReportermanxAssigned Tomanx 
PriorityhighSeveritymajorReproducibilityalways
Status resolvedResolutionfixed 
Product VersionOpenMPT 1.27.00.* (old testing) 
Target VersionOpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first)Fixed in VersionOpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first) 
Summary0000914: 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)

TagsNo tags attached.
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Relationships

related to 0000575 confirmed Inefficient FILE I/O 

Activities

manx

manx

2017-09-17 09:28

administrator  

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-v11.patch (107,955 bytes)
manx

manx

2017-09-20 08:43

administrator  

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-v18.patch (110,517 bytes)
manx

manx

2017-09-21 06:38

administrator  

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;
 	}
probe-refactor-v19.patch (110,567 bytes)
manx

manx

2017-09-21 06:44

administrator   ~0003224

Implemented in r8933.

Issue History

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