View Issue Details

IDProjectCategoryView StatusLast Update
0000914OpenMPTlibopenmptpublic2017-09-21 06:44
Reportermanx Assigned 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.
Attached Files
probe-refactor-v11.patch (107,955 bytes)   
Index: soundlib/ContainerMMCMP.cpp
===================================================================
--- soundlib/ContainerMMCMP.cpp	(revision 8886)
+++ soundlib/ContainerMMCMP.cpp	(working copy)
@@ -13,6 +13,7 @@
 
 #include "../common/FileReader.h"
 #include "Container.h"
+#include "Sndfile.h"
 
 #include <stdexcept>
 
@@ -144,6 +145,66 @@
 }
 
 
+static bool ValidateHeader(const MMCMPFILEHEADER &mfh)
+//----------------------------------------------------
+{
+	if(std::memcmp(mfh.id, "ziRCONia", 8) != 0)
+	{
+		return false;
+	}
+	if(mfh.hdrsize != sizeof(MMCMPHEADER))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static bool ValidateHeader(const MMCMPHEADER &mmh)
+//------------------------------------------------
+{
+	if(mmh.nblocks == 0)
+	{
+		return false;
+	}
+	if(mmh.filesize == 0)
+	{
+		return false;
+	}
+	if(mmh.filesize > 0x80000000)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize)
+//------------------------------------------------------------------------------------------------------
+{
+	MMCMPFILEHEADER mfh;
+	if(!file.ReadStruct(mfh))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(mfh))
+	{
+		return ProbeFailure;
+	}
+	MMCMPHEADER mmh;
+	if(!file.ReadStruct(mmh))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(mmh))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
 //-------------------------------------------------------------------------------------------------------------
 {
@@ -151,14 +212,23 @@
 	containerItems.clear();
 
 	MMCMPFILEHEADER mfh;
-	if(!file.ReadStruct(mfh)) return false;
-	if(std::memcmp(mfh.id, "ziRCONia", 8) != 0) return false;
-	if(mfh.hdrsize != sizeof(MMCMPHEADER)) return false;
+	if(!file.ReadStruct(mfh))
+	{
+		return false;
+	}
+	if(!ValidateHeader(mfh))
+	{
+		return false;
+	}
 	MMCMPHEADER mmh;
-	if(!file.ReadStruct(mmh)) return false;
-	if(mmh.nblocks == 0) return false;
-	if(mmh.filesize == 0) return false;
-	if(mmh.filesize > 0x80000000) return false;
+	if(!file.ReadStruct(mmh))
+	{
+		return false;
+	}
+	if(!ValidateHeader(mmh))
+	{
+		return false;
+	}
 	if(loadFlags == ContainerOnlyVerifyHeader)
 	{
 		return true;
Index: soundlib/ContainerPP20.cpp
===================================================================
--- soundlib/ContainerPP20.cpp	(revision 8886)
+++ soundlib/ContainerPP20.cpp	(working copy)
@@ -13,6 +13,7 @@
 
 #include "../common/FileReader.h"
 #include "Container.h"
+#include "Sndfile.h"
 
 #include <stdexcept>
 
@@ -115,6 +116,51 @@
 }
 
 
+struct PP20header
+{
+	char    magic[4];       // "PP20"
+	uint8be dummy[8];       // unknown
+	uint8be efficiency[4];
+};
+
+MPT_BINARY_STRUCT(PP20header, 16)
+
+
+static bool ValidateHeader(const PP20header &hdr)
+//-----------------------------------------------
+{
+	if(std::memcmp(hdr.magic, "PP20", 4) != 0)
+	{
+		return false;
+	}
+	if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15
+		|| hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15
+		|| hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15
+		|| hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize)
+//-----------------------------------------------------------------------------------------------------
+{
+	PP20header hdr;
+	if(!file.ReadStruct(hdr))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(hdr))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool UnpackPP20(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
 //------------------------------------------------------------------------------------------------------------
 {
@@ -121,15 +167,15 @@
 	file.Rewind();
 	containerItems.clear();
 
-	if(!file.ReadMagic("PP20")) return false;
-	if(!file.CanRead(8)) return false;
-	uint8 efficiency[4];
-	file.ReadArray(efficiency);
-	if(efficiency[0] < 9 || efficiency[0] > 15
-		|| efficiency[1] < 9 || efficiency[1] > 15
-		|| efficiency[2] < 9 || efficiency[2] > 15
-		|| efficiency[3] < 9 || efficiency[3] > 15)
+	PP20header hdr;
+	if(!file.ReadStruct(hdr))
+	{
 		return false;
+	}
+	if(!ValidateHeader(hdr))
+	{
+		return false;
+	}
 	if(loadFlags == ContainerOnlyVerifyHeader)
 	{
 		return true;
Index: soundlib/ContainerUMX.cpp
===================================================================
--- soundlib/ContainerUMX.cpp	(revision 8886)
+++ soundlib/ContainerUMX.cpp	(working copy)
@@ -12,11 +12,48 @@
 #include "Loaders.h"
 #include "UMXTools.h"
 #include "Container.h"
+#include "Sndfile.h"
 
 
 OPENMPT_NAMESPACE_BEGIN
 
 
+static bool ValidateHeader(const UMXFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4)
+		|| fileHeader.nameCount == 0
+		|| fileHeader.exportCount == 0
+		|| fileHeader.importCount == 0
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	UMXFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	if(!FindUMXNameTableEntryMemory(file, fileHeader, "music"))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
 //-----------------------------------------------------------------------------------------------------------
 {
@@ -24,15 +61,14 @@
 	containerItems.clear();
 
 	UMXFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4)
-		|| fileHeader.nameCount == 0
-		|| fileHeader.exportCount == 0
-		|| fileHeader.importCount == 0
-		)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
 	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
 
 	// Note that this can be a false positive, e.g. Unreal maps will have music and sound
 	// in their name table because they usually import such files. However, it spares us
Index: soundlib/ContainerXPK.cpp
===================================================================
--- soundlib/ContainerXPK.cpp	(revision 8886)
+++ soundlib/ContainerXPK.cpp	(working copy)
@@ -13,6 +13,7 @@
 
 #include "../common/FileReader.h"
 #include "Container.h"
+#include "Sndfile.h"
 
 #include <stdexcept>
 
@@ -329,6 +330,68 @@
 }
 
 
+static bool ValidateHeader(const XPKFILEHEADER &header)
+//-----------------------------------------------------
+{
+	if(std::memcmp(header.XPKF, "XPKF", 4) != 0)
+	{
+		return false;
+	}
+	if(std::memcmp(header.SQSH, "SQSH", 4) != 0)
+	{
+		return false;
+	}
+	if(header.SrcLen == 0)
+	{
+		return false;
+	}
+	if(header.DstLen == 0)
+	{
+		return false;
+	}
+	STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8);
+	if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize)
+//------------------------------------------------------------------------------
+{
+	if(filesize != header.SrcLen - 8)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	XPKFILEHEADER header;
+	if(!file.ReadStruct(header))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(header))
+	{
+		return ProbeFailure;
+	}
+	if(pfilesize)
+	{
+		if(!ValidateHeaderFileSize(header, *pfilesize))
+		{
+			return ProbeFailure;
+		}
+	}
+	return ProbeSuccess;
+}
+
+
 bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
 //-----------------------------------------------------------------------------------------------------------
 {
@@ -336,19 +399,24 @@
 	containerItems.clear();
 
 	XPKFILEHEADER header;
-	if(!file.ReadStruct(header)) return false;
-	if(std::memcmp(header.XPKF, "XPKF", 4) != 0) return false;
-	if(std::memcmp(header.SQSH, "SQSH", 4) != 0) return false;
-	if(header.SrcLen == 0) return false;
-	if(header.DstLen == 0) return false;
-	STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8);
-	if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) return false;
+	if(!file.ReadStruct(header))
+	{
+		return false;
+	}
+	if(!ValidateHeader(header))
+	{
+		return false;
+	}
 	if(loadFlags == ContainerOnlyVerifyHeader)
 	{
 		return true;
 	}
-	if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8))) return false;
 
+	if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8)))
+	{
+		return false;
+	}
+
 	containerItems.emplace_back();
 	containerItems.back().data_cache = mpt::make_unique<std::vector<char> >();
 	std::vector<char> & unpackedData = *(containerItems.back().data_cache);
Index: soundlib/Load_669.cpp
===================================================================
--- soundlib/Load_669.cpp	(revision 8886)
+++ soundlib/Load_669.cpp	(working copy)
@@ -62,14 +62,9 @@
 MPT_BINARY_STRUCT(_669Sample, 25)
 
 
-bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static bool ValidateHeader(const _669FileHeader &fileHeader)
 {
-	_669FileHeader fileHeader;
-
-	file.Rewind();
-	if(!file.ReadStruct(fileHeader)
-		|| (memcmp(fileHeader.magic, "if", 2) && memcmp(fileHeader.magic, "JN", 2))
+	if((std::memcmp(fileHeader.magic, "if", 2) && std::memcmp(fileHeader.magic, "JN", 2))
 		|| fileHeader.samples > 64
 		|| fileHeader.restartPos >= 128
 		|| fileHeader.patterns > 128)
@@ -76,21 +71,68 @@
 	{
 		return false;
 	}
-	
-	for(size_t i = 0; i < CountOf(fileHeader.breaks); i++)
+	for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++)
 	{
 		if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE)
+		{
 			return false;
+		}
 		if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0)
+		{
 			return false;
+		}
 		if(fileHeader.breaks[i] >= 64)
+		{
 			return false;
+		}
 	}
+	return true;
+}
 
+
+static uint64 GetHeaderMinimumAdditionalSize(const _669FileHeader &fileHeader)
+//----------------------------------------------------------------------------
+{
+	return fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	_669FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
+bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	_669FileHeader fileHeader;
+
+	file.Rewind();
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
 	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
-	} else if(!file.CanRead(fileHeader.samples * sizeof(_669Sample) + fileHeader.patterns * 1536u))
+	}
+	
+	if(!file.CanRead(GetHeaderMinimumAdditionalSize(fileHeader)))
 	{
 		return false;
 	}
Index: soundlib/Load_amf.cpp
===================================================================
--- soundlib/Load_amf.cpp	(revision 8886)
+++ soundlib/Load_amf.cpp	(working copy)
@@ -80,6 +80,42 @@
 MPT_BINARY_STRUCT(AMFFileHeader, 41)
 
 
+static bool ValidateHeader(const AsylumFileHeader &fileHeader)
+//------------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25)
+		|| fileHeader.numSamples > 64
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const AsylumFileHeader &fileHeader)
+//------------------------------------------------------------------------------
+{
+	return 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize)
+//-----------------------------------------------------------------------------------------------------------
+{
+	AsylumFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags)
 //--------------------------------------------------------------------------
 {
@@ -86,14 +122,16 @@
 	file.Rewind();
 
 	AsylumFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.signature, "ASYLUM Music Format V1.0\0", 25)
-		|| fileHeader.numSamples > 64
-		|| !file.CanRead(256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
@@ -341,6 +379,37 @@
 }
 
 
+static bool ValidateHeader(const AMFFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.amf, "AMF", 3)
+		|| fileHeader.version < 8 || fileHeader.version > 14
+		|| ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10)
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize)
+//---------------------------------------------------------------------------------------------------------
+{
+	AMFFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags)
 //------------------------------------------------------------------------
 {
@@ -347,14 +416,16 @@
 	file.Rewind();
 
 	AMFFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.amf, "AMF", 3)
-		|| fileHeader.version < 8 || fileHeader.version > 14
-		|| ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_ams.cpp
===================================================================
--- soundlib/Load_ams.cpp	(revision 8886)
+++ soundlib/Load_ams.cpp	(working copy)
@@ -332,21 +332,76 @@
 MPT_BINARY_STRUCT(AMSSampleHeader, 17)
 
 
+static bool ValidateHeader(const AMSFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(fileHeader.versionHigh != 0x01)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	if(!file.CanRead(7))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!file.ReadMagic("Extreme"))
+	{
+		return ProbeFailure;
+	}
+	AMSFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
 	file.Rewind();
 
+	if(!file.ReadMagic("Extreme"))
+	{
+		return false;
+	}
 	AMSFileHeader fileHeader;
-	if(!file.ReadMagic("Extreme")
-		|| !file.ReadStruct(fileHeader)
-		|| !file.Skip(fileHeader.extraSize)
-		|| !file.CanRead(3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u)
-		|| fileHeader.versionHigh != 0x01)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(!file.Skip(fileHeader.extraSize))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
@@ -641,30 +696,90 @@
 MPT_BINARY_STRUCT(AMS2Description, 11)
 
 
+static bool ValidateHeader(const AMS2FileHeader &fileHeader)
+//----------------------------------------------------------
+{
+	if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader)
+//----------------------------------------------------------------------------
+{
+	return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize)
+//-----------------------------------------------------------------------------------------------------
+{
+	if(!file.CanRead(7))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!file.ReadMagic("AMShdr\x1A"))
+	{
+		return ProbeFailure;
+	}
+	if(!file.CanRead(1))
+	{
+		return ProbeWantMoreData;
+	}
+	const uint8 songNameLength = file.ReadUint8();
+	if(!file.Skip(songNameLength))
+	{
+		return ProbeWantMoreData;
+	}
+	AMS2FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
 //--------------------------------------------------------------------
 {
 	file.Rewind();
 
-	AMS2FileHeader fileHeader;
 	if(!file.ReadMagic("AMShdr\x1A"))
 	{
 		return false;
 	}
-
-	InitializeGlobals(MOD_TYPE_AMS2);
-
-	if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName)
-		|| !file.ReadStruct(fileHeader)
-		|| fileHeader.versionHigh != 2 || fileHeader.versionLow > 2
-		|| !file.CanRead(36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u))
+	if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	AMS2FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 	
+	InitializeGlobals(MOD_TYPE_AMS2);
+
 	m_nInstruments = fileHeader.numIns;
 	m_nChannels = 32;
 	SetupMODPanning(true);
Index: soundlib/Load_dbm.cpp
===================================================================
--- soundlib/Load_dbm.cpp	(revision 8886)
+++ soundlib/Load_dbm.cpp	(working copy)
@@ -293,19 +293,51 @@
 }
 
 
+static bool ValidateHeader(const DBMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.dbm0, "DBM0", 4)
+		|| fileHeader.trkVerHi > 3)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	DBMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
-	DBMFileHeader fileHeader;
 
 	file.Rewind();
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.dbm0, "DBM0", 4)
-		|| fileHeader.trkVerHi > 3)
+	DBMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_digi.cpp
===================================================================
--- soundlib/Load_digi.cpp	(revision 8886)
+++ soundlib/Load_digi.cpp	(working copy)
@@ -73,6 +73,36 @@
 }
 
 
+static bool ValidateHeader(const DIGIFileHeader &fileHeader)
+{
+	if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20)
+		|| !fileHeader.numChannels
+		|| fileHeader.numChannels > 8
+		|| fileHeader.lastOrdIndex > 127)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize)
+//-----------------------------------------------------------------------------------------------------
+{
+	DIGIFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags)
 //--------------------------------------------------------------------
 {
@@ -79,15 +109,16 @@
 	file.Rewind();
 
 	DIGIFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.signature, "DIGI Booster module\0", 20)
-		|| !fileHeader.numChannels
-		|| fileHeader.numChannels > 8
-		|| fileHeader.lastOrdIndex > 127)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_dmf.cpp
===================================================================
--- soundlib/Load_dmf.cpp	(revision 8886)
+++ soundlib/Load_dmf.cpp	(working copy)
@@ -887,18 +887,51 @@
 }
 
 
+static bool ValidateHeader(const DMFFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.signature, "DDMF", 4)
+		|| !fileHeader.version || fileHeader.version > 10)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	DMFFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
+	file.Rewind();
+
 	DMFFileHeader fileHeader;
-	file.Rewind();
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.signature, "DDMF", 4)
-		|| !fileHeader.version || fileHeader.version > 10)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_dsm.cpp
===================================================================
--- soundlib/Load_dsm.cpp	(revision 8886)
+++ soundlib/Load_dsm.cpp	(working copy)
@@ -100,40 +100,100 @@
 MPT_BINARY_STRUCT(DSMSampleHeader, 64)
 
 
-bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+struct DSMHeader
 {
-	file.Rewind();
+	char fileMagic0[4];
+	char fileMagic1[4];
+	char fileMagic2[4];
+};
 
-	char fileMagic0[4], fileMagic1[4], fileMagic2[4];
-	if(!file.ReadArray(fileMagic0)) return false;
-	if(!file.ReadArray(fileMagic1)) return false;
-	if(!file.ReadArray(fileMagic2)) return false;
+MPT_BINARY_STRUCT(DSMHeader, 12)
 
-	if(!memcmp(fileMagic0, "RIFF", 4)
-		&& !memcmp(fileMagic2, "DSMF", 4))
+
+static bool ValidateHeader(const DSMHeader &fileHeader)
+//-----------------------------------------------------
+{
+	if(!std::memcmp(fileHeader.fileMagic0, "RIFF", 4)
+		&& !std::memcmp(fileHeader.fileMagic2, "DSMF", 4))
 	{
 		// "Normal" DSM files with RIFF header
 		// <RIFF> <file size> <DSMF>
-	} else if(!memcmp(fileMagic0, "DSMF", 4))
+		return true;
+	} else if(!std::memcmp(fileHeader.fileMagic0, "DSMF", 4))
 	{
 		// DSM files with alternative header
 		// <DSMF> <4 bytes, usually 4x NUL or RIFF> <file size> <4 bytes, usually DSMF but not always>
-		file.Skip(4);
+		return true;
 	} else
 	{
 		return false;
 	}
+}
 
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	DSMHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0)
+	{
+		if(!file.Skip(4))
+		{
+			return ProbeWantMoreData;
+		}
+	}
 	DSMChunk chunkHeader;
+	if(!file.ReadStruct(chunkHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(std::memcmp(chunkHeader.magic, "SONG", 4))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
 
-	file.ReadStruct(chunkHeader);
+
+bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+
+	DSMHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(std::memcmp(fileHeader.fileMagic0, "DSMF", 4) == 0)
+	{
+		file.Skip(4);
+	}
+	DSMChunk chunkHeader;
+	if(!file.ReadStruct(chunkHeader))
+	{
+		return false;
+	}
 	// Technically, the song chunk could be anywhere in the file, but we're going to simplify
 	// things by not using a chunk header here and just expect it to be right at the beginning.
-	if(memcmp(chunkHeader.magic, "SONG", 4))
+	if(std::memcmp(chunkHeader.magic, "SONG", 4))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
 	}
Index: soundlib/Load_dtm.cpp
===================================================================
--- soundlib/Load_dtm.cpp	(revision 8886)
+++ soundlib/Load_dtm.cpp	(working copy)
@@ -181,6 +181,37 @@
 MPT_BINARY_STRUCT(DTMText, 12)
 
 
+static bool ValidateHeader(const DTMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "D.T.", 4)
+		|| fileHeader.headerSize < sizeof(fileHeader) - 8u
+		|| fileHeader.headerSize > 256 // Excessively long song title?
+		|| fileHeader.type != 0)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	DTMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -187,15 +218,16 @@
 	file.Rewind();
 
 	DTMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "D.T.", 4)
-		|| fileHeader.headerSize < sizeof(fileHeader) - 8u
-		|| fileHeader.headerSize > 256 // Excessively long song title?
-		|| fileHeader.type != 0)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_far.cpp
===================================================================
--- soundlib/Load_far.cpp	(revision 8886)
+++ soundlib/Load_far.cpp	(working copy)
@@ -102,6 +102,46 @@
 MPT_BINARY_STRUCT(FARSampleHeader, 48)
 
 
+static bool ValidateHeader(const FARFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "FAR\xFE", 4) != 0
+		|| std::memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3)
+		)
+	{
+		return false;
+	}
+	if(fileHeader.headerLength < sizeof(FARFileHeader))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const FARFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return fileHeader.headerLength - sizeof(FARFileHeader);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	FARFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -108,14 +148,20 @@
 	file.Rewind();
 
 	FARFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "FAR\xFE", 4) != 0
-		|| memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3)
-		|| !file.LengthIsAtLeast(fileHeader.headerLength))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_gdm.cpp
===================================================================
--- soundlib/Load_gdm.cpp	(revision 8886)
+++ soundlib/Load_gdm.cpp	(working copy)
@@ -90,28 +90,61 @@
 MPT_BINARY_STRUCT(GDMSampleHeader, 62)
 
 
-bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static const MODTYPE gdmFormatOrigin[] =
 {
-	file.Rewind();
+	MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM
+};
 
-	const MODTYPE gdmFormatOrigin[] =
-	{
-		MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM
-	};
 
-	GDMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "GDM\xFE", 4)
+static bool ValidateHeader(const GDMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "GDM\xFE", 4)
 		|| fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26
-		|| memcmp(fileHeader.magic2, "GMFS", 4)
+		|| std::memcmp(fileHeader.magic2, "GMFS", 4)
 		|| fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0
-		|| fileHeader.originalFormat >= CountOf(gdmFormatOrigin)
+		|| fileHeader.originalFormat >= mpt::size(gdmFormatOrigin)
 		|| fileHeader.originalFormat == 0)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	GDMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
+bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+
+	GDMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_imf.cpp
===================================================================
--- soundlib/Load_imf.cpp	(revision 8886)
+++ soundlib/Load_imf.cpp	(working copy)
@@ -351,19 +351,84 @@
 	}
 }
 
+
+static bool ValidateHeader(const IMFFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.im10, "IM10", 4)
+		|| fileHeader.ordNum > 256
+		|| fileHeader.insNum >= MAX_INSTRUMENTS
+		)
+	{
+		return false;
+	}
+	bool channelFound = false;
+	for(uint8 chn = 0; chn < 32; chn++)
+	{
+		switch(fileHeader.channels[chn].status)
+		{
+		case 0: // enabled; don't worry about it
+			channelFound = true;
+			break;
+		case 1: // mute
+			channelFound = true;
+			break;
+		case 2: // disabled
+			// nothing
+			break;
+		default: // uhhhh.... freak out
+			return false;
+		}
+	}
+	if(!channelFound)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	MPT_UNREFERENCED_PARAMETER(fileHeader);
+	return 256;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	IMFFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
 	IMFFileHeader fileHeader;
 	file.Rewind();
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.im10, "IM10", 4)
-		|| fileHeader.ordNum > 256
-		|| fileHeader.insNum >= MAX_INSTRUMENTS
-		|| !file.CanRead(256))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
 	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
 
 	// Read channel configuration
 	std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled
@@ -397,7 +462,9 @@
 	if(!detectedChannels)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	
+	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
 	}
Index: soundlib/Load_it.cpp
===================================================================
--- soundlib/Load_it.cpp	(revision 8886)
+++ soundlib/Load_it.cpp	(working copy)
@@ -402,6 +402,43 @@
 }
 
 
+static bool ValidateHeader(const ITFileHeader &fileHeader)
+//--------------------------------------------------------
+{
+	if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4))
+		|| fileHeader.insnum > 0xFF
+		|| fileHeader.smpnum >= MAX_SAMPLES
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader)
+//--------------------------------------------------------------------------
+{
+	return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize)
+//---------------------------------------------------------------------------------------------------
+{
+	ITFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
 //------------------------------------------------------------------
 {
@@ -408,15 +445,20 @@
 	file.Rewind();
 
 	ITFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| (memcmp(fileHeader.id, "IMPM", 4) && memcmp(fileHeader.id, "tpm.", 4))
-		|| fileHeader.insnum > 0xFF
-		|| fileHeader.smpnum >= MAX_SAMPLES
-		|| !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_itp.cpp
===================================================================
--- soundlib/Load_itp.cpp	(revision 8886)
+++ soundlib/Load_itp.cpp	(working copy)
@@ -60,6 +60,54 @@
 MPT_BINARY_STRUCT(ITPModCommand, 6)
 
 
+struct ITPHeader
+{
+	uint32le magic;
+	uint32le version;
+};
+
+MPT_BINARY_STRUCT(ITPHeader, 8)
+
+
+static bool ValidateHeader(const ITPHeader &hdr)
+//----------------------------------------------
+{
+	if(hdr.magic != MAGIC4BE('.','i','t','p'))
+	{
+		return false;
+	}
+	if(hdr.version > 0x00000103 || hdr.version < 0x00000100)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr)
+//----------------------------------------------------------------
+{
+	MPT_UNREFERENCED_PARAMETER(hdr);
+	return 12 + 4 + 24 + 4 - sizeof(ITPHeader);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	ITPHeader hdr;
+	if(!file.ReadStruct(hdr))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(hdr))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr));
+}
+
+
 bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------------
 {
@@ -81,22 +129,29 @@
 		ITP_ITPEMBEDIH		= 0x40000,	// Embed instrument headers in project file
 	};
 
-	uint32 version, size;
-
 	file.Rewind();
 
-	// Check file ID
-	if(!file.CanRead(12 + 4 + 24 + 4)
-		|| file.ReadUint32LE() != MAGIC4BE('.','i','t','p')	// Magic bytes
-		|| (version = file.ReadUint32LE()) > 0x00000103		// Format version
-		|| version < 0x00000100)
+	ITPHeader hdr;
+	if(!file.ReadStruct(hdr))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(hdr))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(hdr))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
+	uint32 version, size;
+	version = hdr.version;
+
 	InitializeGlobals(MOD_TYPE_IT);
 	m_playBehaviour.reset();
 	file.ReadSizedString<uint32le, mpt::String::maybeNullTerminated>(m_songName);
Index: soundlib/Load_mdl.cpp
===================================================================
--- soundlib/Load_mdl.cpp	(revision 8886)
+++ soundlib/Load_mdl.cpp	(working copy)
@@ -422,18 +422,50 @@
 }
 
 
+static bool ValidateHeader(const MDLFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.id, "DMDL", 4)
+		|| fileHeader.version >= 0x20)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	MDLFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
 	file.Rewind();
 	MDLFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.id, "DMDL", 4)
-		|| fileHeader.version >= 0x20)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_med.cpp
===================================================================
--- soundlib/Load_med.cpp	(revision 8886)
+++ soundlib/Load_med.cpp	(working copy)
@@ -493,22 +493,68 @@
 }
 
 
+static bool ValidateHeader(const MEDMODULEHEADER &pmmh)
+//-----------------------------------------------------
+{
+	if(std::memcmp(pmmh.id, "MMD", 3)
+		|| pmmh.id[3] < '0' || pmmh.id[3] > '3'
+		|| pmmh.song == 0
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh)
+//-----------------------------------------------------------------------
+{
+	MPT_UNREFERENCED_PARAMETER(pmmh);
+	return sizeof(MMD0SONGHEADER);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	MEDMODULEHEADER pmmh;
+	if(!file.ReadStruct(pmmh))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(pmmh))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh));
+}
+
+
 bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags)
 //------------------------------------------------------------------
 {
 	file.Rewind();
 	MEDMODULEHEADER pmmh;
-	uint32 dwSong;
-	if(!file.CanRead(512)
-		|| !file.ReadStruct(pmmh)
-		|| memcmp(pmmh.id, "MMD", 3)
-		|| pmmh.id[3] < '0' || pmmh.id[3] > '3'
-		|| (dwSong = pmmh.song) == 0
-		|| !file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER)))
+	if(!file.ReadStruct(pmmh))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(pmmh))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(pmmh))))
+	{
+		return false;
+	}
+	const uint32 dwSong = pmmh.song;
+	if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER)))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_mo3.cpp
===================================================================
--- soundlib/Load_mo3.cpp	(revision 8886)
+++ soundlib/Load_mo3.cpp	(working copy)
@@ -691,26 +691,74 @@
 #endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE
 
 
+struct MO3ContainerHeader
+{
+	char     magic[3];   // MO3
+	uint8le  version;
+	uint32le musicSize;
+};
 
+MPT_BINARY_STRUCT(MO3ContainerHeader, 8)
+
+
+static bool ValidateHeader(const MO3ContainerHeader &containerHeader)
+//-------------------------------------------------------------------
+{
+	if(std::memcmp(containerHeader.magic, "MO3", 3))
+	{
+		return false;
+	}
+	if(containerHeader.musicSize <= sizeof(MO3FileHeader))
+	{
+		return false;
+	}
+	if(containerHeader.version > 5)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	MO3ContainerHeader containerHeader;
+	if(!file.ReadStruct(containerHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(containerHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
 	file.Rewind();
 
-	if(!file.CanRead(12) || !file.ReadMagic("MO3"))
+	MO3ContainerHeader containerHeader;
+	if(!file.ReadStruct(containerHeader))
 	{
 		return false;
 	}
-	const uint8 version = file.ReadUint8();
-	const uint32 musicSize = file.ReadUint32LE();
-	if(musicSize <= sizeof(MO3FileHeader) || version > 5)
+	if(!ValidateHeader(containerHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
 	}
 
+	const uint8 version = containerHeader.version;
+	const uint32 musicSize = containerHeader.musicSize;
+
 	uint32 compressedSize = uint32_max;
 	if(version >= 5)
 	{
Index: soundlib/Load_mod.cpp
===================================================================
--- soundlib/Load_mod.cpp	(revision 8886)
+++ soundlib/Load_mod.cpp	(working copy)
@@ -653,20 +653,20 @@
 }
 
 
-bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+struct MODMagicResult
 {
-	char magic[4];
-	if(!file.Seek(1080) || !file.ReadArray(magic))
-	{
-		return false;
-	}
+	CHANNELINDEX m_nChannels       = 4;
+	mpt::ustring m_madeWithTracker = mpt::ustring();
+	bool isNoiseTracker            = false;
+	bool isStartrekker             = false;
+	bool isGenericMultiChannel     = false;
+	bool setMODVBlankTiming        = false;
+};
 
-	InitializeGlobals(MOD_TYPE_MOD);
-	m_nChannels = 4;
-	bool isNoiseTracker = false, isStartrekker = false, isGenericMultiChannel = false;
 
-	// Check MOD Magic
+static bool CheckMODMagic(const char magic[4], MODMagicResult *result)
+{
+	MODMagicResult *r = result;
 	if(IsMagic(magic, "M.K.")		// ProTracker and compatible
 		|| IsMagic(magic, "M!K!")	// ProTracker (>64 patterns)
 		|| IsMagic(magic, "PATT")	// ProTracker 3.6
@@ -673,66 +673,143 @@
 		|| IsMagic(magic, "NSMS")	// kingdomofpleasure.mod by bee hunter
 		|| IsMagic(magic, "LARD"))	// judgement_day_gvine.mod by 4-mat
 	{
-		m_nChannels = 4;
-		m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible");
+		if(r)
+		{
+			r->m_nChannels = 4;
+			r->m_madeWithTracker = MPT_USTRING("Generic ProTracker or compatible");
+		}
 	} else if(IsMagic(magic, "M&K!")	// "His Master's Noise" musicdisk
 		|| IsMagic(magic, "FEST")		// "His Master's Noise" musicdisk
 		|| IsMagic(magic, "N.T."))
 	{
-		m_nChannels = 4;
-		m_madeWithTracker = MPT_USTRING("NoiseTracker");
-		isNoiseTracker = true;
+		if(r)
+		{
+			r->m_nChannels = 4;
+			r->m_madeWithTracker = MPT_USTRING("NoiseTracker");
+			r->isNoiseTracker = true;
+		}
 	} else if(IsMagic(magic, "OKTA")
 		|| IsMagic(magic, "OCTA"))
 	{
 		// Oktalyzer
-		m_nChannels = 8;
-		m_madeWithTracker = MPT_USTRING("Oktalyzer");
+		if(r)
+		{
+			r->m_nChannels = 8;
+			r->m_madeWithTracker = MPT_USTRING("Oktalyzer");
+		}
 	} else if(IsMagic(magic, "CD81")
 		|| IsMagic(magic, "CD61"))
 	{
 		// Octalyser on Atari STe/Falcon
-		m_nChannels = magic[2] - '0';
-		m_madeWithTracker = MPT_USTRING("Octalyser (Atari)");
+		if(r)
+		{
+			r->m_nChannels = magic[2] - '0';
+			r->m_madeWithTracker = MPT_USTRING("Octalyser (Atari)");
+		}
 	} else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8')
 	{
 		// Digital Tracker on Atari Falcon
-		m_nChannels = magic[3] - '0';
-		m_madeWithTracker = MPT_USTRING("Digital Tracker");
+		if(r)
+		{
+			r->m_nChannels = magic[3] - '0';
+			r->m_madeWithTracker = MPT_USTRING("Digital Tracker");
+		}
 	} else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9')
 	{
 		// FLTx / EXOx - Startrekker by Exolon / Fairlight
-		m_nChannels = magic[3] - '0';
-		m_madeWithTracker = MPT_USTRING("Startrekker");
-		isStartrekker = true;
-		m_playBehaviour.set(kMODVBlankTiming);
+		if(r)
+		{
+			r->m_nChannels = magic[3] - '0';
+			r->m_madeWithTracker = MPT_USTRING("Startrekker");
+			r->isStartrekker = true;
+			r->setMODVBlankTiming = true;
+		}
 	} else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3))
 	{
 		// xCHN - Many trackers
-		m_nChannels = magic[0] - '0';
-		m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker");
-		isGenericMultiChannel = true;
+		if(r)
+		{
+			r->m_nChannels = magic[0] - '0';
+			r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker");
+			r->isGenericMultiChannel = true;
+		}
 	} else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9'
 		&& (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2)))
 	{
 		// xxCN / xxCH - Many trackers
-		m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0';
-		m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker");
-		isGenericMultiChannel = true;
+		if(r)
+		{
+			r->m_nChannels = (magic[0] - '0') * 10 + magic[1] - '0';
+			r->m_madeWithTracker = MPT_USTRING("Generic MOD-compatible Tracker");
+			r->isGenericMultiChannel = true;
+		}
 	} else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '4' && magic[3] <= '9')
 	{
 		// TDZx - TakeTracker
-		m_nChannels = magic[3] - '0';
-		m_madeWithTracker = MPT_USTRING("TakeTracker");
+		if(r)
+		{
+			r->m_nChannels = magic[3] - '0';
+			r->m_madeWithTracker = MPT_USTRING("TakeTracker");
+		}
 	} else
 	{
 		return false;
 	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	if(!file.CanRead(1080 + 4))
+	{
+		return ProbeWantMoreData;
+	}
+	file.Seek(1080);
+	char magic[4];
+	file.ReadArray(magic);
+	if(!CheckMODMagic(magic, nullptr))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
+bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	char magic[4];
+	if(!file.Seek(1080) || !file.ReadArray(magic))
+	{
+		return false;
+	}
+
+	InitializeGlobals(MOD_TYPE_MOD);
+
+	MODMagicResult modMagicResult;
+	if(CheckMODMagic(magic, &modMagicResult))
+	{
+		return false;
+	}
+
 	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
 	}
 
+	m_nChannels = modMagicResult.m_nChannels;
+	m_madeWithTracker = modMagicResult.m_madeWithTracker;
+	bool isNoiseTracker = modMagicResult.isNoiseTracker;
+	bool isStartrekker = modMagicResult.isStartrekker;
+	bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel;
+	if(modMagicResult.setMODVBlankTiming)
+	{
+		m_playBehaviour.set(kMODVBlankTiming);
+	}
+
 	LimitMax(m_nChannels, MAX_BASECHANNELS);
 
 	// Startrekker 8 channel mod (needs special treatment, see below)
@@ -1165,8 +1242,8 @@
 
 // Check if a name string is valid (i.e. doesn't contain binary garbage data)
 template<size_t N>
-static uint32 CountInvalidChars(char (&name)[N])
-//----------------------------------------------
+static uint32 CountInvalidChars(const char (&name)[N])
+//----------------------------------------------------
 {
 	uint32 invalidChars = 0;
 	for(auto c : name)
@@ -1194,25 +1271,114 @@
 };
 
 
-bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+
+struct M15FileHeaders
 {
-	file.Rewind();
+	char            songname[20];
+	MODSampleHeader sampleHeaders[15];
+	MODFileHeader   fileHeader;
+};
 
-	char songname[20];
-	file.ReadArray(songname);
+MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130)
 
+
+static bool ValidateHeader(const M15FileHeaders &fileHeaders)
+{
 	// In theory, sample and song names should only ever contain printable ASCII chars and null.
 	// However, there are quite a few SoundTracker modules in the wild with random
 	// characters. To still be able to distguish them from other formats, we just reject
 	// files with *too* many bogus characters. Arbitrary threshold: 20 bogus characters in total
 	// or more than 5 invalid characters just in the title alone.
-	uint32 invalidChars =  CountInvalidChars(songname);
-	if(invalidChars > 5 || !file.CanRead(sizeof(MODSampleHeader) * 15 + sizeof(MODFileHeader)))
+	uint32 invalidChars = CountInvalidChars(fileHeaders.songname);
+	if(invalidChars > 5)
 	{
 		return false;
 	}
 
+	SmpLength totalSampleLen = 0;
+	uint8 allVolumes = 0;
+
+	for(SAMPLEINDEX smp = 0; smp < 15; smp++)
+	{
+		const MODSampleHeader &sampleHeader = fileHeaders.sampleHeaders[smp];
+
+		invalidChars += CountInvalidChars(sampleHeader.name);
+
+		// Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874)
+		if(invalidChars > 48
+			|| sampleHeader.volume > 64
+			|| sampleHeader.finetune != 0
+			|| sampleHeader.length > 32768)
+		{
+			return false;
+		}
+
+		totalSampleLen += sampleHeader.length;
+		allVolumes |= sampleHeader.volume;
+
+	}
+
+	// Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding)
+	if(totalSampleLen == 0 || allVolumes == 0)
+	{
+		return false;
+	}
+
+	// Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220].
+	// There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit.
+	if(fileHeaders.fileHeader.numOrders > 128 || fileHeaders.fileHeader.restartPos > 220)
+	{
+		return false;
+	}
+
+	for(uint8 ord : fileHeaders.fileHeader.orderList)
+	{
+		// Sanity check: 64 patterns max.
+		if(ord > 63)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	M15FileHeaders fileHeaders;
+	if(!file.ReadStruct(fileHeaders))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeaders))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
+bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+
+	M15FileHeaders fileHeaders;
+	if(!file.ReadStruct(fileHeaders))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeaders))
+	{
+		return false;
+	}
+
+	char songname[20];
+	std::memcpy(songname, fileHeaders.songname, 20);
+
 	InitializeGlobals(MOD_TYPE_MOD);
 	m_playBehaviour.reset(kMODOneShotLoops);
 	m_playBehaviour.set(kMODIgnorePanning);
@@ -1223,26 +1389,15 @@
 
 	bool hasDiskNames = true;
 	SmpLength totalSampleLen = 0;
-	uint8 allVolumes = 0;
 	m_nSamples = 15;
 
+	file.Seek(20);
 	for(SAMPLEINDEX smp = 1; smp <= 15; smp++)
 	{
 		MODSampleHeader sampleHeader;
 		ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true);
-		invalidChars += CountInvalidChars(sampleHeader.name);
 
-		// Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874)
-		if(invalidChars > 48
-			|| sampleHeader.volume > 64
-			|| sampleHeader.finetune != 0
-			|| sampleHeader.length > 32768)
-		{
-			return false;
-		}
-
 		totalSampleLen += Samples[smp].nLength;
-		allVolumes |= sampleHeader.volume;
 
 		if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':'))
 		{
@@ -1263,25 +1418,9 @@
 			minVersion = std::max(minVersion, MST1_00);
 	}
 
-	// Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding)
-	if(totalSampleLen == 0 || allVolumes == 0)
-		return false;
-
 	MODFileHeader fileHeader;
 	file.ReadStruct(fileHeader);
 
-	// Sanity check: No more than 128 positions. ST's GUI limits tempo to [1, 220].
-	// There are some mods with a tempo of 0 (explora3-death.mod) though, so ignore the lower limit.
-	if(fileHeader.numOrders > 128 || fileHeader.restartPos > 220)
-		return false;
-
-	for(uint8 ord : fileHeader.orderList)
-	{
-		// Sanity check: 64 patterns max.
-		if(ord > 63)
-			return false;
-	}
-
 	ReadOrderFromArray(Order(), fileHeader.orderList);
 	PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false);
 
@@ -1578,6 +1717,55 @@
 }
 
 
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	if(!file.CanRead(1464 + 4))
+	{
+		return ProbeWantMoreData;
+	}
+	file.Seek(1464);
+	char magic[4];
+	file.ReadArray(magic);
+	if(!IsMagic(magic, "MTN\0") && !IsMagic(magic, "IT10"))
+	{
+		return ProbeFailure;
+	}
+	file.Seek(20);
+	uint32 invalidChars = 0;
+	for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
+	{
+		MODSampleHeader sampleHeader;
+		if(!file.ReadStruct(sampleHeader))
+		{
+		return ProbeWantMoreData;
+		}
+		invalidChars += CountInvalidChars(sampleHeader.name);
+	}
+	if(invalidChars > 256)
+	{
+		return ProbeFailure;
+	}
+	const uint8 numOrders = file.ReadUint8();
+	const uint8 numTracks = file.ReadUint8();
+	if(numOrders > 128)
+	{
+		return ProbeFailure;
+	}
+	uint8 tracks[128 * 4];
+	file.ReadArray(tracks);
+	for(auto track : tracks)
+	{
+		if(track > numTracks)
+		{
+			return ProbeFailure;
+		}
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 // SoundTracker 2.6 / Ice Tracker variation of the MOD format
 // The only real difference to other SoundTracker formats is the way patterns are stored:
 // Every pattern consists of four independent, re-usable tracks.
@@ -1596,11 +1784,15 @@
 	m_playBehaviour.set(kMODSampleSwap);	// untested
 
 	if(IsMagic(magic, "MTN\0"))
+	{
 		m_madeWithTracker = MPT_USTRING("SoundTracker 2.6");
-	else if(IsMagic(magic, "IT10"))
+	} else if(IsMagic(magic, "IT10"))
+	{
 		m_madeWithTracker = MPT_USTRING("Ice Tracker 1.0 / 1.1");
-	else
+	} else
+	{
 		return false;
+	}
 
 	// Reading song title
 	file.Seek(0);
@@ -1622,7 +1814,9 @@
 	const uint8 numOrders = file.ReadUint8();
 	const uint8 numTracks = file.ReadUint8();
 	if(numOrders > 128)
+	{
 		return false;
+	}
 
 	uint8 tracks[128 * 4];
 	file.ReadArray(tracks);
@@ -1629,11 +1823,15 @@
 	for(auto track : tracks)
 	{
 		if(track > numTracks)
+		{
 			return false;
+		}
 	}
 
 	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
+	}
 
 	// Now we can be pretty sure that this is a valid MOD file. Set up default song settings.
 	m_nChannels = 4;
@@ -1731,6 +1929,48 @@
 
 
 
+struct PT36Header
+{
+	char    magicFORM[4];  // "FORM"
+	uint8be dummy1[4];
+	char    magicMODL[4];  // "MODL"
+};
+
+MPT_BINARY_STRUCT(PT36Header, 12);
+
+
+static bool ValidateHeader(const PT36Header &fileHeader)
+//------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magicFORM, "FORM", 4))
+	{
+		return false;
+	}
+	if(std::memcmp(fileHeader.magicMODL, "MODL", 4))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize)
+//-----------------------------------------------------------------------------------------------------
+{
+	PT36Header fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 // ProTracker 3.6 version of the MOD format
 // Basically just a normal ProTracker mod with different magic, wrapped in an IFF file.
 // The "PTDT" chunk is passed to the normal MOD loader.
@@ -1738,16 +1978,20 @@
 //--------------------------------------------------------------------
 {
 	file.Rewind();
-	if(!file.ReadMagic("FORM")
-		|| !file.Skip(4)
-		|| !file.ReadMagic("MODL"))
+
+	PT36Header fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
 	}
-	
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+
 	bool ok = false, infoOk = false;
 	FileReader commentChunk;
-	mpt::ustring version = MPT_USTRING("3.6");
+	mpt::ustring version;
 	PT36InfoChunk info;
 	MemsetZero(info);
 
@@ -1798,6 +2042,11 @@
 			break;
 		}
 	} while(file.ReadStruct(iffHead));
+
+	if(version.empty())
+	{
+		version = MPT_USTRING("3.6");
+	}
 	
 	// both an info chunk and a module are required
 	if(ok && infoOk)
Index: soundlib/Load_mt2.cpp
===================================================================
--- soundlib/Load_mt2.cpp	(revision 8886)
+++ soundlib/Load_mt2.cpp	(working copy)
@@ -395,23 +395,66 @@
 }
 
 
-bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static bool ValidateHeader(const MT2FileHeader &fileHeader)
+//---------------------------------------------------------
 {
-	file.Rewind();
-	MT2FileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.signature, "MT20", 4)
+	if(std::memcmp(fileHeader.signature, "MT20", 4)
 		|| fileHeader.version < 0x200 || fileHeader.version >= 0x300
 		|| fileHeader.numChannels < 1 || fileHeader.numChannels > 64
 		|| fileHeader.numOrders > 256
 		|| fileHeader.numInstruments >= MAX_INSTRUMENTS
 		|| fileHeader.numSamples >= MAX_SAMPLES
-		|| !file.CanRead(256))
+		)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const MT2FileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	MPT_UNREFERENCED_PARAMETER(fileHeader);
+	return 256;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	MT2FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
+bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	MT2FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_mtm.cpp
===================================================================
--- soundlib/Load_mtm.cpp	(revision 8886)
+++ soundlib/Load_mtm.cpp	(working copy)
@@ -75,23 +75,65 @@
 MPT_BINARY_STRUCT(MTMSampleHeader, 37)
 
 
-bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static bool ValidateHeader(const MTMFileHeader &fileHeader)
+//---------------------------------------------------------
 {
-	file.Rewind();
-	MTMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.id, "MTM", 3)
+	if(std::memcmp(fileHeader.id, "MTM", 3)
 		|| fileHeader.version >= 0x20
 		|| fileHeader.lastOrder > 127
 		|| fileHeader.beatsPerTrack > 64
 		|| fileHeader.numChannels > 32
 		|| fileHeader.numChannels == 0
-		|| !file.CanRead(sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize))
+		)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	MTMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
+bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	MTMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_okt.cpp
===================================================================
--- soundlib/Load_okt.cpp	(revision 8886)
+++ soundlib/Load_okt.cpp	(working copy)
@@ -258,6 +258,35 @@
 }
 
 
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	if(!file.CanRead(8))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!file.ReadMagic("OKTASONG"))
+	{
+		return ProbeFailure;
+	}
+	OktIffChunk iffHead;
+	if(!file.ReadStruct(iffHead))
+	{
+		return ProbeWantMoreData;
+	}
+	if(iffHead.chunksize == 0)
+	{
+		return ProbeFailure;
+	}
+	if((iffHead.signature & 0x7f7f7f7fu) != iffHead.signature) // ASCII?
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
Index: soundlib/Load_plm.cpp
===================================================================
--- soundlib/Load_plm.cpp	(revision 8886)
+++ soundlib/Load_plm.cpp	(working copy)
@@ -83,6 +83,44 @@
 MPT_BINARY_STRUCT(PLMOrderItem, 4)
 
 
+static bool ValidateHeader(const PLMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "PLM\x1A", 4)
+		|| fileHeader.version != 0x10
+		|| fileHeader.numChannels == 0 || fileHeader.numChannels > 32
+		|| fileHeader.headerSize < sizeof(PLMFileHeader)
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	PLMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -89,19 +127,28 @@
 	file.Rewind();
 
 	PLMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "PLM\x1A", 4)
-		|| fileHeader.version != 0x10
-		|| fileHeader.numChannels == 0 || fileHeader.numChannels > 32
-		|| !file.Seek(fileHeader.headerSize)
-		|| !file.CanRead(4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples)))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
+	if(!file.Seek(fileHeader.headerSize))
+	{
+		return false;
+	}
+
 	InitializeGlobals(MOD_TYPE_PLM);
 	InitializeChannels();
 	m_SongFlags = SONG_ITOLDEFFECTS;
Index: soundlib/Load_psm.cpp
===================================================================
--- soundlib/Load_psm.cpp	(revision 8886)
+++ soundlib/Load_psm.cpp	(working copy)
@@ -208,10 +208,51 @@
 		offset = 0;
 	}
 	return ConvertStrTo<uint16>(&patternID[offset]);
+}
 
+
+static bool ValidateHeader(const PSMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.formatID, "PSM ", 4)
+		|| std::memcmp(fileHeader.fileInfoID, "FILE", 4))
+	{
+		return false;
+	}
+	return true;
 }
 
 
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	PSMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	PSMChunk chunkHeader;
+	if(!file.ReadStruct(chunkHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(chunkHeader.length == 0)
+	{
+		return ProbeFailure;
+	}
+	if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII?
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -244,8 +285,7 @@
 #endif // MPT_PSM_DECRYPT
 
 	// Check header
-	if(memcmp(fileHeader.formatID, "PSM ", 4)
-		|| memcmp(fileHeader.fileInfoID, "FILE", 4))
+	if(!ValidateHeader(fileHeader))
 	{
 		return false;
 	}
@@ -1029,15 +1069,10 @@
 MPT_BINARY_STRUCT(PSM16PatternHeader, 4)
 
 
-bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags)
-//---------------------------------------------------------------------
+static bool ValidateHeader(const PSM16FileHeader &fileHeader)
+//-----------------------------------------------------------
 {
-	file.Rewind();
-
-	// Is it a valid PSM16 file?
-	PSM16FileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.formatID, "PSM\xFE", 4)
+	if(std::memcmp(fileHeader.formatID, "PSM\xFE", 4)
 		|| fileHeader.lineEnd != 0x1A
 		|| (fileHeader.formatVersion != 0x10 && fileHeader.formatVersion != 0x01) // why is this sometimes 0x01?
 		|| fileHeader.patternVersion != 0 // 255ch pattern version not supported (did anyone use this?)
@@ -1047,8 +1082,45 @@
 		|| std::max(fileHeader.numChannelsPlay, fileHeader.numChannelsReal) == 0)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize)
+//------------------------------------------------------------------------------------------------------
+{
+	PSM16FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
+bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags)
+//---------------------------------------------------------------------
+{
+	file.Rewind();
+
+	// Is it a valid PSM16 file?
+	PSM16FileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_ptm.cpp
===================================================================
--- soundlib/Load_ptm.cpp	(revision 8886)
+++ soundlib/Load_ptm.cpp	(working copy)
@@ -104,14 +104,10 @@
 MPT_BINARY_STRUCT(PTMSampleHeader, 80)
 
 
-bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static bool ValidateHeader(const PTMFileHeader &fileHeader)
+//---------------------------------------------------------
 {
-	file.Rewind();
-
-	PTMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.magic, "PTMF", 4)
+	if(std::memcmp(fileHeader.magic, "PTMF", 4)
 		|| fileHeader.dosEOF != 26
 		|| fileHeader.versionHi > 2
 		|| fileHeader.flags != 0
@@ -120,11 +116,57 @@
 		|| !fileHeader.numOrders || fileHeader.numOrders > 256
 		|| !fileHeader.numSamples || fileHeader.numSamples > 255
 		|| !fileHeader.numPatterns || fileHeader.numPatterns > 128
-		|| !file.CanRead(fileHeader.numSamples * sizeof(PTMSampleHeader)))
+		)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const PTMFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return fileHeader.numSamples * sizeof(PTMSampleHeader);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	PTMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
 	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
+bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+
+	PTMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_s3m.cpp
===================================================================
--- soundlib/Load_s3m.cpp	(revision 8886)
+++ soundlib/Load_s3m.cpp	(working copy)
@@ -172,6 +172,43 @@
 };
 
 
+static bool ValidateHeader(const S3MFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.magic, "SCRM", 4)
+		|| fileHeader.fileType != S3MFileHeader::idS3MType
+		|| (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion)
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	S3MFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -179,15 +216,20 @@
 
 	// Is it a valid S3M file?
 	S3MFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| !file.CanRead(fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2)
-		|| memcmp(fileHeader.magic, "SCRM", 4)
-		|| fileHeader.fileType != S3MFileHeader::idS3MType
-		|| (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_sfx.cpp
===================================================================
--- soundlib/Load_sfx.cpp	(revision 8886)
+++ soundlib/Load_sfx.cpp	(working copy)
@@ -96,6 +96,108 @@
 	return 0;
 }
 
+
+static bool ValidateHeader(const SFXFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(fileHeader.numOrders > 128)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+
+	if(!file.CanRead(0x40))
+	{
+		return ProbeWantMoreData;
+	}
+	if(file.Seek(0x3c) && file.ReadMagic("SONG"))
+	{
+		file.Rewind();
+		for(SAMPLEINDEX smp = 0; smp < 15; smp++)
+		{
+			if(file.ReadUint32BE() > 131072)
+			{
+				return ProbeFailure;
+			}
+		}
+		file.Skip(4);
+		if(!file.CanRead(2))
+		{
+			return ProbeWantMoreData;
+		}
+		uint16 speed = file.ReadUint16BE();
+		if(speed < 178)
+		{
+			return ProbeFailure;
+		}
+		if(!file.CanRead(sizeof(SFXSampleHeader) * 15))
+		{
+			return ProbeWantMoreData;
+		}
+		file.Skip(sizeof(SFXSampleHeader) * 15);
+		SFXFileHeader fileHeader;
+		if(!file.ReadStruct(fileHeader))
+		{
+			return ProbeWantMoreData;
+		}
+		if(!ValidateHeader(fileHeader))
+		{
+			return ProbeFailure;
+		}
+		return ProbeSuccess;
+	}
+
+	if(!file.CanRead(0x80))
+	{
+		return ProbeWantMoreData;
+	}
+	if(file.Seek(0x3c) && file.ReadMagic("SO31"))
+	{
+		file.Rewind();
+		for(SAMPLEINDEX smp = 0; smp < 31; smp++)
+		{
+			if(file.ReadUint32BE() > 131072)
+			{
+				return ProbeFailure;
+			}
+		}
+		file.Skip(4);
+		if(!file.CanRead(2))
+		{
+			return ProbeWantMoreData;
+		}
+		uint16 speed = file.ReadUint16BE();
+		if(speed < 178)
+		{
+			return ProbeFailure;
+		}
+		if(!file.CanRead(sizeof(SFXSampleHeader) * 31))
+		{
+			return ProbeWantMoreData;
+		}
+		file.Skip(sizeof(SFXSampleHeader) * 31);
+		SFXFileHeader fileHeader;
+		if(!file.ReadStruct(fileHeader))
+		{
+			return ProbeWantMoreData;
+		}
+		if(!ValidateHeader(fileHeader))
+		{
+			return ProbeFailure;
+		}
+		return ProbeSuccess;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeFailure;
+}
+
+
 bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -164,12 +266,18 @@
 	}
 
 	SFXFileHeader fileHeader;
-	file.ReadStruct(fileHeader);
-
-	if(fileHeader.numOrders > 128)
+	if(!file.ReadStruct(fileHeader))
+	{
 		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
 	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
+	}
 
 	PATTERNINDEX numPatterns = 0;
 	for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++)
Index: soundlib/Load_stm.cpp
===================================================================
--- soundlib/Load_stm.cpp	(revision 8886)
+++ soundlib/Load_stm.cpp	(working copy)
@@ -94,6 +94,49 @@
 MPT_BINARY_STRUCT(STMPatternData, 4*64*4)
 
 
+static bool ValidateHeader(const STMFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(fileHeader.filetype != 2
+		|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2)	// ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2.
+		|| fileHeader.verMajor != 2
+		|| fileHeader.verMinor > 21	// ST3 only accepts 0, 10, 20 and 21
+		|| fileHeader.globalVolume > 64
+		|| (std::memcmp(fileHeader.trackername, "!Scream!", 8)
+			&& std::memcmp(fileHeader.trackername, "BMOD2STM", 8)
+			&& std::memcmp(fileHeader.trackername, "WUZAMOD!", 8))
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const STMFileHeader &fileHeader)
+//---------------------------------------------------------------------------
+{
+	MPT_UNREFERENCED_PARAMETER(fileHeader);
+	return 31 * sizeof(STMSampleHeader) + 128;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	STMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -105,20 +148,20 @@
 	// After reviewing all STM files on ModLand and ModArchive, it was found that the
 	// case-insensitive comparison is most likely not necessary for any files in the wild.
 	STMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| fileHeader.filetype != 2
-		|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2)	// ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2.
-		|| fileHeader.verMajor != 2
-		|| fileHeader.verMinor > 21	// ST3 only accepts 0, 10, 20 and 21
-		|| fileHeader.globalVolume > 64
-		|| (memcmp(fileHeader.trackername, "!Scream!", 8)
-			&& memcmp(fileHeader.trackername, "BMOD2STM", 8)
-			&& memcmp(fileHeader.trackername, "WUZAMOD!", 8))
-		|| !file.CanRead(31 * sizeof(STMSampleHeader) + 128))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_stp.cpp
===================================================================
--- soundlib/Load_stp.cpp	(revision 8886)
+++ soundlib/Load_stp.cpp	(working copy)
@@ -23,6 +23,7 @@
 // File header (except for "STP3" magic)
 struct STPFileHeader
 {
+	char     magic[4];
 	uint16be version;
 	uint8be  numOrders;
 	uint8be  patternLength;
@@ -38,7 +39,7 @@
 	uint16be sampleStructSize;
 };
 
-MPT_BINARY_STRUCT(STPFileHeader, 200);
+MPT_BINARY_STRUCT(STPFileHeader, 204);
 
 
 // Sample header (common part between all versions)
@@ -203,24 +204,57 @@
 }
 
 
-bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
-//-------------------------------------------------------------------
+static bool ValidateHeader(const STPFileHeader &fileHeader)
+//---------------------------------------------------------
 {
-	file.Rewind();
-	if(!file.ReadMagic("STP3"))
-		return false;
-
-	STPFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
+	if(std::memcmp(fileHeader.magic, "STP3", 4)
 		|| fileHeader.version > 2
 		|| fileHeader.numOrders > 128
 		|| fileHeader.numSamples >= MAX_SAMPLES
 		|| fileHeader.timerCount == 0
 		|| fileHeader.midiCount != 50)
+	{
 		return false;
+	}
+	return true;
+}
 
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	STPFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+
+	STPFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
 	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
+	}
 
 	InitializeGlobals(MOD_TYPE_STP);
 
Index: soundlib/Load_ult.cpp
===================================================================
--- soundlib/Load_ult.cpp	(revision 8886)
+++ soundlib/Load_ult.cpp	(working copy)
@@ -336,21 +336,53 @@
 };
 
 
+static bool ValidateHeader(const UltFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(fileHeader.version < '1'
+		|| fileHeader.version > '4'
+		|| std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature))
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	UltFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
 	file.Rewind();
+
 	UltFileHeader fileHeader;
-
-	// Tracker ID
-	if(!file.ReadStruct(fileHeader)
-		|| fileHeader.version < '1'
-		|| fileHeader.version > '4'
-		|| memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature)) != 0)
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(!ValidateHeader(fileHeader))
 	{
+		return false;
+	}
+	if(loadFlags == onlyVerifyHeader)
+	{
 		return true;
 	}
 
Index: soundlib/Load_xm.cpp
===================================================================
--- soundlib/Load_xm.cpp	(revision 8886)
+++ soundlib/Load_xm.cpp	(working copy)
@@ -262,6 +262,43 @@
 DECLARE_FLAGSET(TrackerVersions)
 
 
+static bool ValidateHeader(const XMFileHeader &fileHeader)
+//--------------------------------------------------------
+{
+	if(fileHeader.channels == 0
+		|| fileHeader.channels > MAX_BASECHANNELS
+		|| std::memcmp(fileHeader.signature, "Extended Module: ", 17)
+		)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static uint64 GetHeaderMinimumAdditionalSize(const XMFileHeader &fileHeader)
+//--------------------------------------------------------------------------
+{
+	return fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments);
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize)
+//---------------------------------------------------------------------------------------------------
+{
+	XMFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
+}
+
+
 bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
 //------------------------------------------------------------------
 {
@@ -268,13 +305,17 @@
 	file.Rewind();
 
 	XMFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| fileHeader.channels == 0
-		|| fileHeader.channels > MAX_BASECHANNELS
-		|| memcmp(fileHeader.signature, "Extended Module: ", 17)
-		|| !file.CanRead(fileHeader.orders + 4 * (fileHeader.patterns + fileHeader.instruments)))
+	if(!file.ReadStruct(fileHeader))
 	{
 		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
+	{
+		return false;
 	} else if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
Index: soundlib/Sndfile.cpp
===================================================================
--- soundlib/Sndfile.cpp	(revision 8886)
+++ soundlib/Sndfile.cpp	(working copy)
@@ -214,9 +214,113 @@
 const std::size_t CSoundFile::ProbeRecommendedSize = 2048;
 
 
+CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize)
+//------------------------------------------------------------------------------------------------------------------------------------
+{
+	const uint64 availableFileSize = file.GetLength();
+	const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength());
+	//const uint64 validFileSize = std::min<uint64>(fileSize, ProbeRecommendedSize);
+	const uint64 goalSize = file.GetPosition() + minimumAdditionalSize;
+	//const uint64 goalMinimumSize = std::min<uint64>(goalSize, ProbeRecommendedSize);
+	if(pfilesize)
+	{
+		if(availableFileSize < std::min<uint64>(fileSize, ProbeRecommendedSize))
+		{
+			if(availableFileSize < goalSize)
+			{
+				return ProbeWantMoreData;
+			}
+		} else
+		{
+			if(fileSize < goalSize)
+			{
+				return ProbeFailure;
+			}
+		}
+		return ProbeSuccess;
+	}
+#if 0
+	if(!pfilesize)
+	{
+		if(fileSize < goalSize && fileSize < ProbeRecommendedSize)
+		{
+			return ProbeWantMoreData;
+		}
+		return ProbeSuccess;
+	}
+#else
+	return ProbeSuccess;
+#endif
+}
+
+
+#define MPT_DO_PROBE( storedResult , call ) \
+	MPT_DO { \
+		ProbeResult lastResult = call ; \
+		if(lastResult == ProbeSuccess) { \
+			return ProbeSuccess; \
+		} else if(lastResult == ProbeWantMoreData) { \
+			storedResult = ProbeWantMoreData; \
+		} \
+	} MPT_WHILE_0 \
+/**/
+
+
 CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize)
 //-------------------------------------------------------------------------------------------------------------------
 {
+#if 1
+	ProbeResult result = ProbeFailure;
+	MemoryFileReader file(data);
+	if(flags & ProbeContainers)
+	{
+		MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize));
+	}
+	if(flags & ProbeModules)
+	{
+		MPT_DO_PROBE(result, ProbeFileHeader669(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderAM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderAMF_Asylum(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderAMF_DSMI(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderAMS(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderAMS2(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderDBM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderDTM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderDIGI(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderDMF(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderDSM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderFAR(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderGDM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderICE(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderIMF(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderIT(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderITP(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderJ2B(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderM15(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMDL(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMED(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMO3(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMOD(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMT2(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderMTM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderOKT(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPLM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPSM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPSM16(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPT36(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderPTM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderS3M(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderSFX(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderSTM(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderSTP(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderULT(file, pfilesize));
+		MPT_DO_PROBE(result, ProbeFileHeaderXM(file, pfilesize));
+	}
+	return result;
+#else
 	uint64 filesize = 0;
 	if(pfilesize)
 	{
@@ -257,6 +361,7 @@
 	}
 	sndFile->Destroy();
 	return ProbeSuccess;
+#endif
 }
 
 
Index: soundlib/Sndfile.h
===================================================================
--- soundlib/Sndfile.h	(revision 8886)
+++ soundlib/Sndfile.h	(working copy)
@@ -579,6 +579,8 @@
 		ProbeWantMoreData = -1
 	};
 
+	static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize);
+
 	static ProbeResult Probe(ProbeFlags flags, mpt::span<const mpt::byte> data, const uint64 *pfilesize);
 
 public:
@@ -670,6 +672,49 @@
 	bool InitChannel(CHANNELINDEX nChn);
 	void InitAmigaResampler();
 
+	static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize);
+
+	static ProbeResult ProbeFileHeader669(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderAMF_Asylum(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderDMF(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderDSM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderFAR(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderICE(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMO3(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMT2(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPSM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPSM16(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPT36(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderPTM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderSTM(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize);
+	static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize);
+
 	// Module Loaders
 	bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
@@ -692,7 +737,6 @@
 	bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMed(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
-	bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMod(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
@@ -708,9 +752,11 @@
 	bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+	bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+
+	bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
-	bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 
 	static std::vector<const char *> GetSupportedExtensions(bool otherFormats);
 	static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored
Index: soundlib/UMXTools.cpp
===================================================================
--- soundlib/UMXTools.cpp	(revision 8886)
+++ soundlib/UMXTools.cpp	(working copy)
@@ -17,8 +17,9 @@
 
 
 // Read compressed unreal integers - similar to MIDI integers, but signed values are possible.
-int32 ReadUMXIndex(FileReader &chunk)
-//-----------------------------------
+template <typename Tfile>
+static int32 ReadUMXIndexImpl(Tfile &chunk)
+//-----------------------------------------
 {
 	enum
 	{
@@ -55,10 +56,17 @@
 	return result;
 }
 
+int32 ReadUMXIndex(FileReader &chunk)
+//-----------------------------------
+{
+	return ReadUMXIndexImpl(chunk);
+}
 
+
 // Returns true if the given nme exists in the name table.
-bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name)
-//---------------------------------------------------------------------------------------------
+template <typename TFile>
+static bool FindUMXNameTableEntryImpl(TFile &file, const UMXFileHeader &fileHeader, const char *name)
+//---------------------------------------------------------------------------------------------------
 {
 	if(!name)
 	{
@@ -77,7 +85,7 @@
 		{
 			if(fileHeader.packageVersion >= 64)
 			{
-				int32 length = ReadUMXIndex(file);
+				int32 length = ReadUMXIndexImpl(file);
 				if(length <= 0)
 				{
 					continue;
@@ -110,7 +118,19 @@
 	return result;
 }
 
+bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name)
+//---------------------------------------------------------------------------------------------
+{
+	return FindUMXNameTableEntryImpl(file, fileHeader, name);
+}
 
+bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name)
+//---------------------------------------------------------------------------------------------------------
+{
+	return FindUMXNameTableEntryImpl(file, fileHeader, name);
+}
+
+
 // Read an entry from the name table.
 std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion)
 //-------------------------------------------------------------------------
Index: soundlib/UMXTools.h
===================================================================
--- soundlib/UMXTools.h	(revision 8886)
+++ soundlib/UMXTools.h	(working copy)
@@ -38,6 +38,9 @@
 // Returns true if the given nme exists in the name table.
 bool FindUMXNameTableEntry(FileReader &file, const UMXFileHeader &fileHeader, const char *name);
 
+// Returns true if the given nme exists in the name table.
+bool FindUMXNameTableEntryMemory(MemoryFileReader &file, const UMXFileHeader &fileHeader, const char *name);
+
 // Read an entry from the name table.
 std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion);
 
Index: soundlib/load_j2b.cpp
===================================================================
--- soundlib/load_j2b.cpp	(revision 8886)
+++ soundlib/load_j2b.cpp	(working copy)
@@ -615,6 +615,66 @@
 }
 
 
+struct AMFFRiffChunkFormat
+{
+	uint32le format;
+};
+
+MPT_BINARY_STRUCT(AMFFRiffChunkFormat, 4)
+
+
+static bool ValidateHeader(const AMFFRiffChunk &fileHeader)
+//---------------------------------------------------------
+{
+	if(fileHeader.id != AMFFRiffChunk::idRIFF)
+	{
+		return false;
+	}
+	if(fileHeader.GetLength() < 8 + sizeof(AMFFMainChunk))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static bool ValidateHeader(const AMFFRiffChunkFormat &formatHeader)
+//-----------------------------------------------------------------
+{
+	if(formatHeader.format != AMFFRiffChunk::idAMFF || formatHeader.format != AMFFRiffChunk::idAM__)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAM(MemoryFileReader file, const uint64 *pfilesize)
+//---------------------------------------------------------------------------------------------------
+{
+	AMFFRiffChunk fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	AMFFRiffChunkFormat formatHeader;
+	if(!file.ReadStruct(formatHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(formatHeader))
+	{
+		return ProbeFailure;
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags)
 //------------------------------------------------------------------
 {
@@ -624,15 +684,23 @@
 	{
 		return false;
 	}
-
-	if(fileHeader.id != AMFFRiffChunk::idRIFF)
+	if(!ValidateHeader(fileHeader))
 	{
 		return false;
 	}
+	AMFFRiffChunkFormat formatHeader;
+	if(!file.ReadStruct(formatHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(formatHeader))
+	{
+		return false;
+	}
 
 	bool isAM; // false: AMFF, true: AM
 
-	uint32 format = file.ReadUint32LE();
+	uint32 format = formatHeader.format;
 	if(format == AMFFRiffChunk::idAMFF)
 		isAM = false; // "AMFF"
 	else if(format == AMFFRiffChunk::idAM__)
@@ -877,6 +945,64 @@
 	return true;
 }
 
+
+static bool ValidateHeader(const J2BFileHeader &fileHeader)
+//---------------------------------------------------------
+{
+	if(std::memcmp(fileHeader.signature, "MUSE", 4)
+		|| (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM)
+			&& fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF)
+		)
+	{
+		return false;
+	}
+	if(fileHeader.packedLength == 0)
+	{
+		return false;
+	}
+	if(fileHeader.fileLength != fileHeader.packedLength + sizeof(J2BFileHeader))
+	{
+		return false;
+	}
+	return true;
+}
+
+
+static bool ValidateHeaderFileSize(const J2BFileHeader &fileHeader, uint64 filesize)
+//----------------------------------------------------------------------------------
+{
+	if(filesize != fileHeader.fileLength)
+	{
+		return false;
+	}
+	return true;
+}
+
+
+CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize)
+//----------------------------------------------------------------------------------------------------
+{
+	J2BFileHeader fileHeader;
+	if(!file.ReadStruct(fileHeader))
+	{
+		return ProbeWantMoreData;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return ProbeFailure;
+	}
+	if(pfilesize)
+	{
+		if(!ValidateHeaderFileSize(fileHeader, *pfilesize))
+		{
+			return ProbeFailure;
+		}
+	}
+	MPT_UNREFERENCED_PARAMETER(pfilesize);
+	return ProbeSuccess;
+}
+
+
 bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags)
 //-------------------------------------------------------------------
 {
@@ -891,17 +1017,21 @@
 
 	file.Rewind();
 	J2BFileHeader fileHeader;
-	if(!file.ReadStruct(fileHeader)
-		|| memcmp(fileHeader.signature, "MUSE", 4)
-		|| (fileHeader.deadbeaf != J2BFileHeader::magicDEADBEAF // 0xDEADBEAF (RIFF AM)
-			&& fileHeader.deadbeaf != J2BFileHeader::magicDEADBABE) // 0xDEADBABE (RIFF AMFF)
-		|| fileHeader.fileLength != file.GetLength()
+	if(!file.ReadStruct(fileHeader))
+	{
+		return false;
+	}
+	if(!ValidateHeader(fileHeader))
+	{
+		return false;
+	}
+	if(fileHeader.fileLength != file.GetLength()
 		|| fileHeader.packedLength != file.BytesLeft()
-		|| fileHeader.packedLength == 0
 		)
 	{
 		return false;
-	} else if(loadFlags == onlyVerifyHeader)
+	}
+	if(loadFlags == onlyVerifyHeader)
 	{
 		return true;
 	}
probe-refactor-v11.patch (107,955 bytes)   
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)   
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)   
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Relationships

related to 0000575 resolvedmanx Inefficient FILE I/O 

Activities

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