View Issue Details

IDProjectCategoryView StatusLast Update
0000755OpenMPTFile Format Supportpublic2017-03-31 08:02
ReporterRevenant Assigned ToSaga Musix  
PrioritynormalSeverityminorReproducibilityN/A
Status resolvedResolutionfixed 
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) 
Summary0000755: Soundtracker Pro II support
Description

As mentioned the other day, here's my experimental loader patch for Soundtracker Pro II.

Program & documentation:
http://www.pouet.net/prod.php?which=34746
Example files:
ftp://ftp.modland.com/pub/modules/SoundTracker%20Pro%20II/

Some notes:

  • The file format supports an arbitrary number of channels (as a per-pattern setting, for some reason), but the editor is fixed at 4 channels like most other Amiga trackers. The loader still uses the "true" number of channels.

  • Most of STP2's effects are the kind that automatically keep going until you play a new note or turn the effect off (usually using param 00). The loader will try to combine effects as much as possible but some less-important effects may occasionally drop out depending on the complexity of the song. However, all the .stp files I can find are either directly converted from Protracker or have little to no effect usage to begin with, so in practice this might not be a huge problem.

  • STP2 uses Amiga CIA clock periods instead of BPM tempo values. The loader will try convert to BPM accurately.

  • STP2 uses the same speed values as other Soundtracker/Protracker versions, but also supports fractional speed values (i.e. between speeds 6 and 7 there is also 6.25, 6.5, and 6.75). These are simulated by inserting fine pattern delays at appropriate intervals wherever possible; not a very elegant solution but this doesn't seem to be a frequently-used feature anyway. (I originally came up with a much more accurate solution based on the "alternative" tempo mode, but then it pretty much completely ruined effect parameters as-is so I ditched it.)

  • Features like multiple loops per sample, "scripts" (programmable pattern lists), MIDI settings etc. are ignored, as well as effects related to those features (plus a couple of weird effects like "change clock rate so that this note loops 'y' times across 'x' patterns" which I really didn't want to try to do the math for).

Anyway, let me know what you think. I'd like to be able to find more files to test with than the very few on Modland, but I think all of the ones I have currently get loaded/played back as close as possible to how they sound in the original tracker.

TagsNo tags attached.
Attached Files
stpro2.patch (23,636 bytes)   
diff --git OpenMPT/libopenmpt/foo_openmpt.cpp OpenMPT/libopenmpt/foo_openmpt.cpp
index ac7ce34..8947cdf 100644
--- OpenMPT/libopenmpt/foo_openmpt.cpp
+++ OpenMPT/libopenmpt/foo_openmpt.cpp
@@ -319,6 +319,7 @@ DECLARE_FILE_TYPE("OpenMPT compatible module files",
 	"*.imf" ";"
 	"*.j2b" ";"
 	"*.plm" ";"
+	"*.stp" ";"
 	"*.gdm" ";"
 	"*.umx" ";"
 	"*.mo3" ";"
diff --git OpenMPT/soundlib/Load_stp.cpp OpenMPT/soundlib/Load_stp.cpp
new file mode 100644
index 0000000..457ecdf
--- /dev/null
+++ OpenMPT/soundlib/Load_stp.cpp
@@ -0,0 +1,716 @@
+/*
+ * Load_stp.cpp
+ * ------------
+ * Purpose: STP (Soundtracker Pro II) module loader
+ * Notes  : doesn't support multiple loops per sample, and various exotic effects.
+ *          fractional speed values and combined auto effects are handled whenever possible,
+ *          but some effects may be omitted (and there may be tempo accuracy issues).
+ * Authors: Devin Acker
+ *          OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ *
+ * Wisdom from the Soundtracker Pro II manual:
+ * "To create shorter patterns, simply create shorter patterns."
+ */
+ 
+#include "stdafx.h"
+#include "Loaders.h"
+#include "Tables.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(push, 1)
+#endif
+
+// File header (except for "STP3" magic)
+struct PACKED STPFileHeader {
+	uint16 version;
+	uint8  numOrders;
+	uint8  patternLength;
+	uint8  orderList[128];
+	uint16 speed;
+	uint16 speedFrac;
+	uint16 timerCount;
+	uint16 flags;
+	uint32 reserved;
+	uint16 midiCount; // always 50
+	uint8  midi[50];
+	uint16 numSamples;
+	uint16 sampleStructSize;
+	
+	void ConvertEndianness()
+	{
+		SwapBytesBE(version);
+		SwapBytesBE(speed);
+		SwapBytesBE(speedFrac);
+		SwapBytesBE(timerCount);
+		SwapBytesBE(flags);
+		SwapBytesBE(midiCount);
+		SwapBytesBE(numSamples);
+		SwapBytesBE(sampleStructSize);
+	}
+};
+
+STATIC_ASSERT(sizeof(STPFileHeader) == 200);
+
+// Sample header (versions 0 and 1)
+struct PACKED STPSampleHeaderOld {
+	char   pathName[31];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint32 reserved2;
+	
+	void ConvertEndianness()
+	{
+		SwapBytesBE(length);
+		SwapBytesBE(loopStart);
+		SwapBytesBE(loopLength);
+		SwapBytesBE(defaultCmd);
+	}
+	
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nVolume = 4 * MIN(volume, 64);
+		
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+		
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+	
+	void Read(FileReader &file) {
+		file.ReadConvertEndianness(*this);
+		pathName[30] = '\0';
+	}
+};
+
+STATIC_ASSERT(sizeof(STPSampleHeaderOld) == 82);
+
+// Sample header (version 2), not packed due to variable string length and alignment in file
+struct STPSampleHeader {
+	char   pathName[256];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint16 defaultPeriod;
+	uint8  finetune;
+	uint8  reserved2;
+	
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nFineTune = static_cast<int8>(finetune << 3);
+		mptSmp.nVolume = 4 * MIN(volume, 64);
+		
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+		
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+	
+	void Read(FileReader &file) {
+		std::string str;
+		
+		file.ReadNullString(str, 255);
+		std::strncpy(pathName, str.c_str(), 255);
+		pathName[255] = '\0';
+		
+		flags = file.ReadUint8();
+		
+		file.ReadNullString(str, 29);
+		std::strncpy(fileName, str.c_str(), 29);
+		pathName[29] = '\0';
+		
+		// seek to even boundary
+		if(file.GetPosition() & 1)
+			file.Skip(1);
+		
+		length = file.ReadUint32BE();
+		volume = file.ReadUint8();
+		reserved1 = file.ReadUint8();
+		
+		loopStart = file.ReadUint32BE();
+		loopLength = file.ReadUint32BE();
+		
+		defaultCmd = file.ReadUint16BE();
+		defaultPeriod = file.ReadUint16BE();
+		
+		finetune = file.ReadUint8();
+		reserved2 = file.ReadUint8();
+	}
+};
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(pop)
+#endif
+
+template <typename T>
+static void ReadSample(FileReader &file, T &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME])
+//--------------------------------------------------------------------------------------------------------------
+{
+	sampleHeader.Read(file);
+	sampleHeader.ConvertToMPT(sample);
+
+	mpt::String::Read<mpt::String::spacePadded>(sampleName, sampleHeader.fileName);
+	// Get rid of weird characters in sample names.
+	for(size_t i = 0; i < CountOf(sampleName); i++)
+	{
+		if(sampleName[i] > 0 && sampleName[i] < ' ')
+		{
+			sampleName[i] = ' ';
+		}
+	}
+}
+
+static TEMPO ConvertTempo(uint16 ciaSpeed)
+//---------------------------------------
+{
+	// 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
+	return TEMPO(125.0 * (3546.0 / ciaSpeed));
+}
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	if(!file.ReadMagic("STP3"))
+		return false;
+
+	STPFileHeader fileHeader;
+	file.ReadConvertEndianness(fileHeader);
+	if(fileHeader.version > 2 || 
+	   fileHeader.numOrders > 128 || fileHeader.numSamples > MAX_SAMPLES ||
+	   fileHeader.midiCount != 50)
+		return false;
+	
+	if(loadFlags == onlyVerifyHeader)
+		return true;
+		
+	InitializeGlobals(MOD_TYPE_STP);
+	
+	m_nChannels = 4;
+	m_nSamples = 0;
+	m_nInstruments = 0;
+	
+	m_nDefaultSpeed = fileHeader.speed;
+	m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
+	
+	m_nMinPeriod = 14 * 4;
+	m_nMaxPeriod = 3424 * 4;
+	
+	Order.ReadFromArray(fileHeader.orderList, fileHeader.numOrders);
+
+	// Load sample headers
+	for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++)
+	{
+		// this is 1-based the same as smp
+		SAMPLEINDEX actualSmp = file.ReadUint16BE();
+		if(actualSmp > MAX_SAMPLES)
+			return false;
+		
+		m_nSamples = MAX(m_nSamples, actualSmp);
+	
+		if(fileHeader.version == 2)
+		{
+			static STPSampleHeader sampleHeader;
+			uint32 headerSize = file.ReadUint32BE();
+			
+			ReadSample(file, sampleHeader, Samples[actualSmp], m_szNames[actualSmp]);
+			// TODO: verify string lengths against headerSize?
+			(void)headerSize;
+		}
+		else
+		{
+			static STPSampleHeaderOld sampleHeaderOld;
+			ReadSample(file, sampleHeaderOld, Samples[actualSmp], m_szNames[actualSmp]);
+		}
+		
+		if(fileHeader.version >= 1)
+		{
+			uint16 numLoops = file.ReadUint16BE();
+			// ignore these
+			file.Skip(numLoops*8);
+		}
+	}
+
+	// Load patterns
+	uint16 numPatterns = 128;
+	if (fileHeader.version == 0)
+		numPatterns = file.ReadUint16BE();
+	
+	uint16 patternLength = fileHeader.patternLength;
+	uint16 channels = 4;
+	
+	uint8 globalVolSlide = 0;
+	std::vector<uint8> autoFinePorta;
+	std::vector<uint8> autoPortaUp;
+	std::vector<uint8> autoPortaDown;
+	std::vector<uint8> autoVolSlide;
+	std::vector<uint8> autoVibrato;
+	std::vector<uint8> vibratoMem;
+	std::vector<uint8> autoTremolo;
+	std::vector<uint8> autoTonePorta;
+	std::vector<uint8> tonePortaMem;
+	
+	for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
+	{
+		PATTERNINDEX actualPat = pat;
+		
+		if(fileHeader.version > 0)
+		{
+			actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+			
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+			
+			m_nChannels = MIN(m_nChannels, channels);
+		}
+		
+		if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
+		{
+			file.Skip(channels * patternLength * 4);
+			continue;
+		}
+		
+		autoFinePorta.resize(channels, 0);
+		autoPortaUp.resize(channels, 0);
+		autoPortaDown.resize(channels, 0);
+		autoVolSlide.resize(channels, 0);
+		autoVibrato.resize(channels, 0);
+		vibratoMem.resize(channels, 0);
+		autoTremolo.resize(channels, 0);
+		autoTonePorta.resize(channels, 0);
+		tonePortaMem.resize(channels, 0);
+		
+		for(ROWINDEX row = 0; row < patternLength; row++)
+		{
+			PatternRow rowBase = Patterns[actualPat].GetpModCommand(row, 0);
+
+			bool didGlobalVolSlide = false;
+			bool shouldDelay = false;
+			
+			// if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
+			switch(fileHeader.speedFrac & 3)
+			{
+			// 1/4
+			case 1: shouldDelay = (row & 3) == 0; break;
+			// 1/2
+			case 2: shouldDelay = (row & 1) == 0; break;
+			// 3/4
+			case 3: shouldDelay = (row & 3) != 3; break;
+			}
+			
+			for(CHANNELINDEX chn = 0; chn < channels; chn++)
+			{
+				ModCommand &m = rowBase[chn];
+				
+				m.instr   = file.ReadUint8();
+				m.note    = file.ReadUint8();
+				m.command = file.ReadUint8();
+				m.param   = file.ReadUint8();
+				
+				if(m.note)
+				{
+					m.note += 25;
+					
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoVolSlide[chn] = 0;
+					autoVibrato[chn] = vibratoMem[chn] = 0;
+					autoTremolo[chn] = 0;
+					autoTonePorta[chn] = tonePortaMem[chn] = 0;
+				}
+				
+				uint8 swap = (m.param >> 4) | (m.param << 4);
+				
+				if((m.command & 0xF0) == 0xF0)
+				{
+					m.param = Util::Round<uint8>(ConvertTempo(m.param | (((uint16)m.command & 0xF) << 8)).ToDouble());
+					m.command = CMD_TEMPO;
+				} else switch(m.command)
+				{
+				case 0x00: // arpeggio
+					if(m.param)
+						m.command = CMD_ARPEGGIO; 
+					break;
+					
+				case 0x01: // portamento up
+					m.command = CMD_PORTAMENTOUP; 
+					break;
+					
+				case 0x02: // portamento down
+					m.command = CMD_PORTAMENTODOWN; 
+					break;
+				
+				case 0x03: // auto fine portamento up
+					autoFinePorta[chn] = 0x10 | MIN(m.param, 15);
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+					
+				case 0x04: // auto fine portamento down
+					autoFinePorta[chn] = 0x20 | MIN(m.param, 15);
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x05: // auto portamento up
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = m.param;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x06: // auto portamento down
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = m.param;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x07: // set global volume
+					m.command = CMD_GLOBALVOLUME;
+					globalVolSlide = 0;
+					break;
+				
+				case 0x08: // auto global volume slide
+					globalVolSlide = swap;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x09: // fine portamento up
+					m.command = CMD_MODCMDEX;
+					m.param = 0x10 | MIN(m.param, 15);
+					break;
+					
+				case 0x0A: // fine portamento down
+					m.command = CMD_MODCMDEX;
+					m.param = 0x20 | MIN(m.param, 15);
+					break;
+				
+				case 0x0B: // auto volume slide
+					autoVolSlide[chn] = swap;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x0C: // set volume
+					m.volcmd = VOLCMD_VOLUME;
+					m.vol = m.param;
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x0D: // volume slide (param is swapped compared to .mod)
+					if(m.param & 0xF0)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEDOWN;
+						m.vol = m.param >> 4;
+					} else if(m.param & 0x0F)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEUP;
+						m.vol = m.param & 0xF;
+					}
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x0E: // set filter (also uses opposite value compared to .mod)
+					m.command = CMD_MODCMDEX;
+					m.param = 1 ^ MIN(m.param, 1);
+					break;
+					
+				case 0x0F: // set speed
+					m.command = CMD_SPEED;
+					fileHeader.speedFrac = m.param & 0xF;
+					m.param >>= 4; 
+					break;
+				
+				case 0x10: // auto vibrato
+					autoVibrato[chn] = m.param;
+					vibratoMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x11: // auto tremolo
+					if(m.param & 0xF)
+						autoTremolo[chn] = m.param;
+					else
+						autoTremolo[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x12: // pattern break
+					m.command = CMD_PATTERNBREAK; 
+					break;
+				
+				case 0x13: // auto tone portamento
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = m.param;
+				
+					tonePortaMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x14: // position jump
+					m.command = CMD_POSITIONJUMP; 
+					break;
+				
+				case 0x1D: // fine volume slide
+					m.command = CMD_MODCMDEX;
+					if(m.param & 0xF0)
+						m.param = 0xB0 | (m.param >> 4);
+					else if(m.param & 0x0F)
+						m.param = 0xA0 | (m.param & 0xF);
+					break;
+				
+				case 0x20: // "delayed fade"
+					// just behave like either a normal fade or a notecut
+					// depending on the speed
+					if(m.param & 0xF0)
+					{
+						autoVolSlide[chn] = m.param >> 4;
+						m.command = m.param = 0;
+					} else {
+						m.command = CMD_MODCMDEX;
+						m.param = 0xC0 | (m.param & 0xF);
+					}
+					break;
+				
+				case 0x21: // note delay
+					m.command = CMD_MODCMDEX;
+					m.param = 0xD0 | MIN(m.param, 15);
+					break;
+				
+				case 0x22: // retrigger note
+					m.command = CMD_RETRIG;
+					break;
+				
+				case 0x49: // set sample offset
+					m.command = CMD_OFFSET; 
+					break;
+				
+				case 0x4E: // other protracker commands (pattern loop / delay)
+					if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
+						m.command = CMD_MODCMDEX;
+					else
+						m.command = m.param = 0;
+					break;
+				
+				case 0x4F: // set speed/tempo
+					if(m.param < 0x20)
+					{
+						m.command = CMD_SPEED;
+						fileHeader.speedFrac = 0;
+					} else
+					{
+						m.command = CMD_TEMPO;
+					}
+					break;
+				
+				default:
+					m.command = m.param = 0; 
+					break;
+				}
+				
+				bool didVolSlide = false;
+				
+				// try to put volume slide in volume command
+				if(autoVolSlide[chn] && !m.volcmd)
+				{
+					if(autoVolSlide[chn] & 0xF0)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEDOWN;
+						m.vol = autoVolSlide[chn] >> 4;
+					} else
+					{
+						m.volcmd = VOLCMD_VOLSLIDEUP;
+						m.vol = autoVolSlide[chn] & 0xF;
+					}
+					didVolSlide = true;
+				}
+				
+				// try to place/combine all remaining running effects.
+				if(!m.command)
+				{
+					if(autoPortaUp[chn])
+					{
+						m.command = CMD_PORTAMENTOUP;
+						m.param = autoPortaUp[chn];
+						
+					} else if(autoPortaDown[chn])
+					{
+						m.command = CMD_PORTAMENTODOWN;
+						m.param = autoPortaDown[chn];
+						
+					} else if(autoFinePorta[chn])
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = autoFinePorta[chn];
+						
+					} else if(autoTonePorta[chn])
+					{
+						// try a volslide+portamento
+						if (!didVolSlide && autoVolSlide[chn] && tonePortaMem[chn])
+						{
+							m.command = CMD_TONEPORTAVOL;
+							m.param = autoVolSlide[chn];
+							didVolSlide = true;
+						} else
+						{
+							m.command = CMD_TONEPORTAMENTO;
+							m.param = tonePortaMem[chn] = autoTonePorta[chn];
+						}
+						
+					} else if(autoVibrato[chn])
+					{
+						// try a volslide+vibrato
+						if (!didVolSlide && autoVolSlide[chn] && vibratoMem[chn])
+						{
+							m.command = CMD_VIBRATOVOL;
+							m.param = autoVolSlide[chn];
+							didVolSlide = true;
+						} else
+						{
+							m.command = CMD_VIBRATO;
+							m.param = vibratoMem[chn] = autoVibrato[chn];
+						}
+						
+					} else if(!didVolSlide && autoVolSlide[chn])
+					{
+						m.command = CMD_VOLUMESLIDE;
+						m.param = autoVolSlide[chn];
+						didVolSlide = true;
+						
+					} else if(autoTremolo[chn])
+					{
+						m.command = CMD_TREMOLO;
+						m.param = autoTremolo[chn];
+						
+					} else if(shouldDelay)
+					{
+						// insert a fine pattern delay here
+						m.command = CMD_S3MCMDEX;
+						m.param = 0x61;
+						shouldDelay = false;
+					
+					} else if(!didGlobalVolSlide && globalVolSlide)
+					{
+						m.command = CMD_GLOBALVOLSLIDE;
+						m.param = globalVolSlide;
+						didGlobalVolSlide = true;
+					}
+				}
+			}
+			
+			// TODO: create/use extra channels for global volslide/delay if needed
+		}
+	}
+
+	// after we know how many channels there really are...
+	m_nSamplePreAmp = 256 / m_nChannels;
+	// Setup channel pan positions and volume
+	SetupMODPanning(true);
+
+	// Skip over scripts and drumpad info
+	if(fileHeader.version > 0)
+	{
+		uint16 scriptNum;
+		uint32 length;
+		
+		while(file.CanRead(2))
+		{
+			scriptNum = file.ReadUint16BE();
+			if(scriptNum == 0xFFFF)
+				break;
+			
+			file.Skip(2);
+			length = file.ReadUint32BE();
+			file.Skip(length);
+		}
+		
+		// skip drumpad stuff
+		file.Skip(17*2);
+	}
+
+	// Reading samples
+	if(loadFlags & loadSampleData)
+	{
+		for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) if(Samples[smp].nLength)
+		{
+			SampleIO(
+				SampleIO::_8bit,
+				SampleIO::mono,
+				SampleIO::littleEndian,
+				SampleIO::signedPCM)
+				.ReadSample(Samples[smp], file);
+		}
+	}
+
+	return true;
+}
+
+OPENMPT_NAMESPACE_END
diff --git OpenMPT/soundlib/Snd_defs.h OpenMPT/soundlib/Snd_defs.h
index 4499f0b..23eeb62 100644
--- OpenMPT/soundlib/Snd_defs.h
+++ OpenMPT/soundlib/Snd_defs.h
@@ -97,6 +97,7 @@ enum MODTYPE
 	MOD_TYPE_DIGI	= 0x8000000,
 	MOD_TYPE_UAX	= 0x10000000, // sampleset as module
 	MOD_TYPE_PLM	= 0x20000000,
+	MOD_TYPE_STP	= 0x40000000,
 };
 DECLARE_FLAGSET(MODTYPE)
 
diff --git OpenMPT/soundlib/Snd_fx.cpp OpenMPT/soundlib/Snd_fx.cpp
index 1631620..80bcb87 100644
--- OpenMPT/soundlib/Snd_fx.cpp
+++ OpenMPT/soundlib/Snd_fx.cpp
@@ -3358,7 +3358,7 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, param, doFineSlides);
@@ -3417,7 +3417,7 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
@@ -3889,7 +3889,7 @@ void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param)
 	else
 		param = pChn->nOldVolumeSlide;
 
-	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
+	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP)))
 	{
 		// MOD / XM nibble priority
 		if((param & 0xF0) != 0)
@@ -3902,7 +3902,7 @@ void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param)
 	}
 
 	int newvolume = pChn->nVolume;
-	if(!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_MED)))
+	if(!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_STP)))
 	{
 		if ((param & 0x0F) == 0x0F) //Fine upslide or slide -15
 		{
@@ -5445,7 +5445,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio
 //------------------------------------------------------------------------------------------
 {
 	if (!period) return 0;
-	if (GetType() & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_DIGI|MOD_TYPE_MTM|MOD_TYPE_AMF0))
+	if (GetType() & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_DIGI|MOD_TYPE_MTM|MOD_TYPE_AMF0|MOD_TYPE_STP))
 	{
 		return ((3546895L * 4) << FREQ_FRACBITS) / period;
 	} else if (GetType() == MOD_TYPE_XM)
diff --git OpenMPT/soundlib/Sndfile.cpp OpenMPT/soundlib/Sndfile.cpp
index 006d046..39e4a03 100644
--- OpenMPT/soundlib/Sndfile.cpp
+++ OpenMPT/soundlib/Sndfile.cpp
@@ -317,7 +317,8 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
 		 && !ReadMod(file, loadFlags)
 		 && !Read669(file, loadFlags)
 		 && !ReadICE(file, loadFlags)
-		 && !ReadM15(file, loadFlags))
+		 && !ReadM15(file, loadFlags)
+		 && !ReadSTP(file, loadFlags))
 		{
 			m_nType = MOD_TYPE_NONE;
 			m_ContainerType = MOD_CONTAINERTYPE_NONE;
@@ -1046,6 +1047,8 @@ MODTYPE CSoundFile::GetBestSaveFormat() const
 	case MOD_TYPE_AMF0:
 	case MOD_TYPE_DIGI:
 		return MOD_TYPE_MOD;
+	case MOD_TYPE_STP:
+		return MOD_TYPE_XM;
 	case MOD_TYPE_MED:
 		if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments)
 		{
diff --git OpenMPT/soundlib/Sndfile.h OpenMPT/soundlib/Sndfile.h
index 1ea882a..1c03f2a 100644
--- OpenMPT/soundlib/Sndfile.h
+++ OpenMPT/soundlib/Sndfile.h
@@ -659,6 +659,7 @@ public:
 	bool ReadDIGI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadPLM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+	bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 
 	static std::vector<const char *> GetSupportedExtensions(bool otherFormats);
 	static mpt::Charset GetCharsetFromModType(MODTYPE modtype);
diff --git OpenMPT/soundlib/Tables.cpp OpenMPT/soundlib/Tables.cpp
index abf91a9..06250bd 100644
--- OpenMPT/soundlib/Tables.cpp
+++ OpenMPT/soundlib/Tables.cpp
@@ -87,6 +87,7 @@ static const ModFormatInfo modFormatInfo[] =
 	{ MOD_TYPE_IMF,		"Imago Orpheus",			"imf" },
 	{ MOD_TYPE_J2B,		"Galaxy Sound System",		"j2b" },
 	{ MOD_TYPE_PLM,		"Disorder Tracker 2",		"plm" },
+	{ MOD_TYPE_STP,		"Soundtracker Pro II",		"stp" },
 
 #ifndef NO_ARCHIVE_SUPPORT
 	// Compressed modules
@@ -147,6 +148,7 @@ static const ModCharsetInfo ModCharsetInfos[] =
 	{ MOD_TYPE_OKT , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DBM , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DIGI, mpt::CharsetISO8859_1  },
+	{ MOD_TYPE_STP,  mpt::CharsetISO8859_1  },
 	// Amiga // DOS
 	{ MOD_TYPE_MOD , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_MED , mpt::CharsetISO8859_1  },
stpro2.patch (23,636 bytes)   
stpro2_r2.patch (32,311 bytes)   
diff --git a/OpenMPT/build/android_ndk/Android-minimp3-stbvorbis.mk b/OpenMPT/build/android_ndk/Android-minimp3-stbvorbis.mk
index 7ec6552..05ad6fb 100644
--- a/OpenMPT/build/android_ndk/Android-minimp3-stbvorbis.mk
+++ b/OpenMPT/build/android_ndk/Android-minimp3-stbvorbis.mk
@@ -74,6 +74,7 @@ LOCAL_SRC_FILES := \
 	soundlib/Load_ptm.cpp \
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
diff --git a/OpenMPT/build/android_ndk/Android-mpg123-vorbis.mk b/OpenMPT/build/android_ndk/Android-mpg123-vorbis.mk
index 0c09b5e..456aa21 100644
--- a/OpenMPT/build/android_ndk/Android-mpg123-vorbis.mk
+++ b/OpenMPT/build/android_ndk/Android-mpg123-vorbis.mk
@@ -72,6 +72,7 @@ LOCAL_SRC_FILES := \
 	soundlib/Load_ptm.cpp \
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
diff --git a/OpenMPT/build/android_ndk/Android-unmo3.mk b/OpenMPT/build/android_ndk/Android-unmo3.mk
index f28d829..aa56d61 100644
--- a/OpenMPT/build/android_ndk/Android-unmo3.mk
+++ b/OpenMPT/build/android_ndk/Android-unmo3.mk
@@ -105,6 +105,7 @@ LOCAL_SRC_FILES := \
 	soundlib/Load_ptm.cpp \
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
diff --git a/OpenMPT/build/android_ndk/Android.mk b/OpenMPT/build/android_ndk/Android.mk
index 6ef628b..6643057 100644
--- a/OpenMPT/build/android_ndk/Android.mk
+++ b/OpenMPT/build/android_ndk/Android.mk
@@ -73,6 +73,7 @@ LOCAL_SRC_FILES := \
 	soundlib/Load_ptm.cpp \
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
diff --git a/OpenMPT/build/autotools/Makefile.am b/OpenMPT/build/autotools/Makefile.am
index 86dbdd2..ea60f94 100644
--- a/OpenMPT/build/autotools/Makefile.am
+++ b/OpenMPT/build/autotools/Makefile.am
@@ -185,6 +185,7 @@ libopenmpt_la_SOURCES += soundlib/Load_psm.cpp
 libopenmpt_la_SOURCES += soundlib/Load_ptm.cpp
 libopenmpt_la_SOURCES += soundlib/Load_s3m.cpp
 libopenmpt_la_SOURCES += soundlib/Load_stm.cpp
+libopenmpt_la_SOURCES += soundlib/Load_stp.cpp
 libopenmpt_la_SOURCES += soundlib/Load_ult.cpp
 libopenmpt_la_SOURCES += soundlib/Load_umx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_wav.cpp
@@ -418,6 +419,7 @@ libopenmpttest_SOURCES += soundlib/Load_psm.cpp
 libopenmpttest_SOURCES += soundlib/Load_ptm.cpp
 libopenmpttest_SOURCES += soundlib/Load_s3m.cpp
 libopenmpttest_SOURCES += soundlib/Load_stm.cpp
+libopenmpttest_SOURCES += soundlib/Load_stp.cpp
 libopenmpttest_SOURCES += soundlib/Load_ult.cpp
 libopenmpttest_SOURCES += soundlib/Load_umx.cpp
 libopenmpttest_SOURCES += soundlib/Load_wav.cpp
diff --git a/OpenMPT/libopenmpt/foo_openmpt.cpp b/OpenMPT/libopenmpt/foo_openmpt.cpp
index ac7ce34..8947cdf 100644
--- a/OpenMPT/libopenmpt/foo_openmpt.cpp
+++ b/OpenMPT/libopenmpt/foo_openmpt.cpp
@@ -319,6 +319,7 @@ DECLARE_FILE_TYPE("OpenMPT compatible module files",
 	"*.imf" ";"
 	"*.j2b" ";"
 	"*.plm" ";"
+	"*.stp" ";"
 	"*.gdm" ";"
 	"*.umx" ";"
 	"*.mo3" ";"
diff --git a/OpenMPT/soundlib/Load_stp.cpp b/OpenMPT/soundlib/Load_stp.cpp
new file mode 100644
index 0000000..2a054ee
--- /dev/null
+++ b/OpenMPT/soundlib/Load_stp.cpp
@@ -0,0 +1,945 @@
+/*
+ * Load_stp.cpp
+ * ------------
+ * Purpose: STP (Soundtracker Pro II) module loader
+ * Notes  : a few exotic effects aren't supported.
+ *          multiple sample loops are supported, but only the first 10 can be used as cue points
+ *          (with 16xx and 18xx).
+ *          fractional speed values and combined auto effects are handled whenever possible,
+ *          but some effects may be omitted (and there may be tempo accuracy issues).
+ * Authors: Devin Acker
+ *          OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ *
+ * Wisdom from the Soundtracker Pro II manual:
+ * "To create shorter patterns, simply create shorter patterns."
+ */
+ 
+#include "stdafx.h"
+#include "Loaders.h"
+#include "Tables.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(push, 1)
+#endif
+
+// File header (except for "STP3" magic)
+struct PACKED STPFileHeader {
+	uint16 version;
+	uint8  numOrders;
+	uint8  patternLength;
+	uint8  orderList[128];
+	uint16 speed;
+	uint16 speedFrac;
+	uint16 timerCount;
+	uint16 flags;
+	uint32 reserved;
+	uint16 midiCount; // always 50
+	uint8  midi[50];
+	uint16 numSamples;
+	uint16 sampleStructSize;
+	
+	void ConvertEndianness()
+	{
+		SwapBytesBE(version);
+		SwapBytesBE(speed);
+		SwapBytesBE(speedFrac);
+		SwapBytesBE(timerCount);
+		SwapBytesBE(flags);
+		SwapBytesBE(midiCount);
+		SwapBytesBE(numSamples);
+		SwapBytesBE(sampleStructSize);
+	}
+};
+
+STATIC_ASSERT(sizeof(STPFileHeader) == 200);
+
+// Sample header (versions 0 and 1)
+struct PACKED STPSampleHeaderOld {
+	char   pathName[31];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint32 reserved2;
+	
+	void ConvertEndianness()
+	{
+		SwapBytesBE(length);
+		SwapBytesBE(loopStart);
+		SwapBytesBE(loopLength);
+		SwapBytesBE(defaultCmd);
+	}
+	
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nVolume = 4 * MIN(volume, 64);
+		
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+		
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+	
+	void Read(FileReader &file) {
+		file.ReadConvertEndianness(*this);
+		pathName[30] = '\0';
+	}
+};
+
+STATIC_ASSERT(sizeof(STPSampleHeaderOld) == 82);
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(pop)
+#endif
+
+// Sample header (version 2), not packed due to variable string length and alignment in file
+struct STPSampleHeader {
+	char   pathName[256];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint16 defaultPeriod;
+	uint8  finetune;
+	uint8  reserved2;
+	
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nFineTune = static_cast<int8>(finetune << 3);
+		mptSmp.nVolume = 4 * MIN(volume, 64);
+		
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+		
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.cues[0] = mptSmp.nLoopStart;
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+	
+	void Read(FileReader &file) {
+		std::string str;
+		
+		file.ReadNullString(str, 255);
+		std::strncpy(pathName, str.c_str(), 255);
+		pathName[255] = '\0';
+		
+		flags = file.ReadUint8();
+		
+		file.ReadNullString(str, 29);
+		std::strncpy(fileName, str.c_str(), 29);
+		pathName[29] = '\0';
+		
+		// seek to even boundary
+		if(file.GetPosition() & 1)
+			file.Skip(1);
+		
+		length = file.ReadUint32BE();
+		volume = file.ReadUint8();
+		reserved1 = file.ReadUint8();
+		
+		loopStart = file.ReadUint32BE();
+		loopLength = file.ReadUint32BE();
+		
+		defaultCmd = file.ReadUint16BE();
+		defaultPeriod = file.ReadUint16BE();
+		
+		finetune = file.ReadUint8();
+		reserved2 = file.ReadUint8();
+	}
+};
+
+struct STPLoopInfo {
+	SmpLength loopStart;
+	SmpLength loopLength;
+	SAMPLEINDEX looped;
+	SAMPLEINDEX nonLooped;
+};
+
+typedef std::vector<STPLoopInfo> STPLoopList;
+
+template <typename T>
+static void ReadSample(FileReader &file, T &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME])
+//--------------------------------------------------------------------------------------------------------------
+{
+	sampleHeader.Read(file);
+	sampleHeader.ConvertToMPT(sample);
+
+	mpt::String::Read<mpt::String::spacePadded>(sampleName, sampleHeader.fileName);
+	// Get rid of weird characters in sample names.
+	for(size_t i = 0; i < CountOf(sampleName); i++)
+	{
+		if(sampleName[i] > 0 && sampleName[i] < ' ')
+		{
+			sampleName[i] = ' ';
+		}
+	}
+}
+
+static TEMPO ConvertTempo(uint16 ciaSpeed)
+//----------------------------------------
+{
+	// 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
+	return TEMPO(125.0 * (3546.0 / ciaSpeed));
+}
+
+static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
+//------------------------------------------------------------------------------------------------------
+{
+	if(!src.HasSampleData()) return;
+	
+	dest.FreeSample();
+	
+	dest = src;
+	dest.nLength = len;
+	dest.pSample = nullptr;
+	
+	if(dest.AllocateSample() != len)
+	{
+		dest.FreeSample();
+		return;
+	}
+	
+	// only preserve cue points if the target sample length is the same
+	if(len != src.nLength)
+		memset(dest.cues, 0, sizeof(dest.cues));
+	
+	memcpy(dest.pSample8, src.pSample8 + start, len);
+	if(loop)
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = len;
+		dest.uFlags.set(CHN_LOOP);
+	} else
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = 0;
+		dest.uFlags.reset(CHN_LOOP);
+	}
+}
+
+static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
+//--------------------------------------------------------------------
+{
+	// This should only modify a sample if it has more than one loop
+	// (otherwise, it behaves like a normal sample loop)
+	if(!smp.HasSampleData() || loopList.size() < 2) return;
+	
+	ModSample newSmp = smp;
+	newSmp.nLength = 0;
+	newSmp.pSample = nullptr;
+	
+	SAMPLEINDEX numLoops = loopList.size();
+	
+	// get the total length of the sample after combining all looped sections
+	for(int i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+		
+		// if adding this loop would cause the sample length to exceed maximum, 
+		// then limit and bail out
+		if((newSmp.nLength + info.loopLength > MAX_SAMPLE_LENGTH) || 
+		   (info.loopLength > MAX_SAMPLE_LENGTH) ||
+		   (info.loopStart + info.loopLength > smp.nLength))
+		{
+			numLoops = i;
+			break;
+		}
+		
+		newSmp.nLength += info.loopLength;
+	}
+	
+	if(newSmp.AllocateSample() != newSmp.nLength)
+	{
+		newSmp.FreeSample();
+		return;
+	}
+	
+	// start copying the looped sample data parts
+	SmpLength start = 0;
+		
+	for(int i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+		
+		memcpy(newSmp.pSample8 + start, smp.pSample8 + info.loopStart, info.loopLength);
+		
+		// update loop info based on position in edited sample
+		info.loopStart = start;
+		if(i > 0 && i < 10)
+		{
+			newSmp.cues[i-1] = start;
+		}
+		start += info.loopLength;
+	}
+	
+	// replace old sample with new one
+	smp.FreeSample();
+	smp = newSmp;
+	
+	smp.nLoopStart = 0;
+	smp.nLoopEnd = smp.nLength;
+	smp.uFlags.set(CHN_LOOP);
+}
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	if(!file.ReadMagic("STP3"))
+		return false;
+
+	STPFileHeader fileHeader;
+	file.ReadConvertEndianness(fileHeader);
+	if(fileHeader.version > 2 || 
+	   fileHeader.numOrders > 128 || fileHeader.numSamples > MAX_SAMPLES ||
+	   fileHeader.midiCount != 50)
+		return false;
+	
+	if(loadFlags == onlyVerifyHeader)
+		return true;
+		
+	InitializeGlobals(MOD_TYPE_STP);
+	
+	m_nChannels = 4;
+	m_nSamples = 0;
+	m_nInstruments = 0;
+	
+	m_nDefaultSpeed = fileHeader.speed;
+	m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
+	
+	m_nMinPeriod = 14 * 4;
+	m_nMaxPeriod = 3424 * 4;
+	
+	Order.ReadFromArray(fileHeader.orderList, fileHeader.numOrders);
+
+	std::vector<STPLoopList> loopInfo;
+	// non-looped versions of samples with loopes (when needed)
+	std::vector<SAMPLEINDEX> nonLooped;
+
+	// Load sample headers
+	SAMPLEINDEX samplesInFile = 0;
+	
+	for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++)
+	{
+		// this is 1-based the same as smp
+		SAMPLEINDEX actualSmp = file.ReadUint16BE();
+		if(actualSmp > MAX_SAMPLES)
+			return false;
+		
+		samplesInFile = m_nSamples = MAX(m_nSamples, actualSmp);
+		
+		ModSample &thisSmp = Samples[actualSmp];
+		
+		if(fileHeader.version == 2)
+		{
+			STPSampleHeader sampleHeader;
+			uint32 headerSize = file.ReadUint32BE();
+			
+			ReadSample(file, sampleHeader, thisSmp, m_szNames[actualSmp]);
+			// TODO: verify string lengths against headerSize?
+			(void)headerSize;
+		} else
+		{
+			STPSampleHeaderOld sampleHeaderOld;
+			ReadSample(file, sampleHeaderOld, thisSmp, m_szNames[actualSmp]);
+		}
+		
+		STPLoopList loopList;
+		
+		if(fileHeader.version >= 1)
+		{
+			uint16 numLoops = file.ReadUint16BE();
+			
+			STPLoopInfo loop;
+			loop.looped = loop.nonLooped = 0;
+				
+			if(numLoops == 0 && thisSmp.uFlags[CHN_LOOP])
+			{
+				loop.loopStart  = thisSmp.nLoopStart;
+				loop.loopLength = thisSmp.nLoopEnd - thisSmp.nLoopStart;
+				loopList.push_back(loop);
+				
+			} else for(SAMPLEINDEX i = 0; i < numLoops; i++)
+			{
+				loop.loopStart  = file.ReadUint32BE();
+				loop.loopLength = file.ReadUint32BE();
+				loopList.push_back(loop);
+			}
+		}
+		
+		nonLooped.resize(actualSmp);
+		loopInfo.resize(actualSmp);
+		loopInfo[actualSmp - 1] = loopList;
+	}
+
+	// Load patterns
+	uint16 numPatterns = 128;
+	if (fileHeader.version == 0)
+		numPatterns = file.ReadUint16BE();
+	
+	uint16 patternLength = fileHeader.patternLength;
+	uint16 channels = 4;
+	
+	uint8 globalVolSlide = 0;
+	std::vector<uint8> autoFinePorta;
+	std::vector<uint8> autoPortaUp;
+	std::vector<uint8> autoPortaDown;
+	std::vector<uint8> autoVolSlide;
+	std::vector<uint8> autoVibrato;
+	std::vector<uint8> vibratoMem;
+	std::vector<uint8> autoTremolo;
+	std::vector<uint8> autoTonePorta;
+	std::vector<uint8> tonePortaMem;
+	
+	for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
+	{
+		PATTERNINDEX actualPat = pat;
+		
+		if(fileHeader.version > 0)
+		{
+			actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+			
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+			
+			m_nChannels = MIN(m_nChannels, channels);
+		}
+		
+		if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
+		{
+			file.Skip(channels * patternLength * 4);
+			continue;
+		}
+		
+		autoFinePorta.resize(channels, 0);
+		autoPortaUp.resize(channels, 0);
+		autoPortaDown.resize(channels, 0);
+		autoVolSlide.resize(channels, 0);
+		autoVibrato.resize(channels, 0);
+		vibratoMem.resize(channels, 0);
+		autoTremolo.resize(channels, 0);
+		autoTonePorta.resize(channels, 0);
+		tonePortaMem.resize(channels, 0);
+		
+		for(ROWINDEX row = 0; row < patternLength; row++)
+		{
+			PatternRow rowBase = Patterns[actualPat].GetpModCommand(row, 0);
+
+			bool didGlobalVolSlide = false;
+			bool shouldDelay = false;
+			
+			// if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
+			switch(fileHeader.speedFrac & 3)
+			{
+			// 1/4
+			case 1: shouldDelay = (row & 3) == 0; break;
+			// 1/2
+			case 2: shouldDelay = (row & 1) == 0; break;
+			// 3/4
+			case 3: shouldDelay = (row & 3) != 3; break;
+			}
+			
+			for(CHANNELINDEX chn = 0; chn < channels; chn++)
+			{
+				ModCommand &m = rowBase[chn];
+				
+				m.instr   = file.ReadUint8();
+				m.note    = file.ReadUint8();
+				m.command = file.ReadUint8();
+				m.param   = file.ReadUint8();
+				
+				if(m.note)
+				{
+					m.note += 25;
+					
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoVolSlide[chn] = 0;
+					autoVibrato[chn] = vibratoMem[chn] = 0;
+					autoTremolo[chn] = 0;
+					autoTonePorta[chn] = tonePortaMem[chn] = 0;
+				}
+				
+				// this is a nibble-swapped param value used for auto fine volside
+				// and auto global fine volside
+				uint8 swap = (m.param >> 4) | (m.param << 4);
+				
+				if((m.command & 0xF0) == 0xF0)
+				{
+					m.param = Util::Round<uint8>(ConvertTempo(m.param | (((uint16)m.command & 0xF) << 8)).ToDouble());
+					m.command = CMD_TEMPO;
+				} else switch(m.command)
+				{
+				case 0x00: // arpeggio
+					if(m.param)
+						m.command = CMD_ARPEGGIO; 
+					break;
+					
+				case 0x01: // portamento up
+					m.command = CMD_PORTAMENTOUP; 
+					break;
+					
+				case 0x02: // portamento down
+					m.command = CMD_PORTAMENTODOWN; 
+					break;
+				
+				case 0x03: // auto fine portamento up
+					autoFinePorta[chn] = 0x10 | MIN(m.param, 15);
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+					
+				case 0x04: // auto fine portamento down
+					autoFinePorta[chn] = 0x20 | MIN(m.param, 15);
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x05: // auto portamento up
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = m.param;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x06: // auto portamento down
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = m.param;
+					autoTonePorta[chn] = 0;
+				
+					m.command = m.param = 0;
+					break;
+				
+				case 0x07: // set global volume
+					m.command = CMD_GLOBALVOLUME;
+					globalVolSlide = 0;
+					break;
+				
+				case 0x08: // auto global fine volume slide
+					globalVolSlide = swap;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x09: // fine portamento up
+					m.command = CMD_MODCMDEX;
+					m.param = 0x10 | MIN(m.param, 15);
+					break;
+					
+				case 0x0A: // fine portamento down
+					m.command = CMD_MODCMDEX;
+					m.param = 0x20 | MIN(m.param, 15);
+					break;
+				
+				case 0x0B: // auto fine volume slide
+					autoVolSlide[chn] = swap;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x0C: // set volume
+					m.volcmd = VOLCMD_VOLUME;
+					m.vol = m.param;
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x0D: // volume slide (param is swapped compared to .mod)
+					if(m.param & 0xF0)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEDOWN;
+						m.vol = m.param >> 4;
+					} else if(m.param & 0x0F)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEUP;
+						m.vol = m.param & 0xF;
+					}
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x0E: // set filter (also uses opposite value compared to .mod)
+					m.command = CMD_MODCMDEX;
+					m.param = 1 ^ MIN(m.param, 1);
+					break;
+					
+				case 0x0F: // set speed
+					m.command = CMD_SPEED;
+					fileHeader.speedFrac = m.param & 0xF;
+					m.param >>= 4; 
+					break;
+				
+				case 0x10: // auto vibrato
+					autoVibrato[chn] = m.param;
+					vibratoMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+					
+				case 0x11: // auto tremolo
+					if(m.param & 0xF)
+						autoTremolo[chn] = m.param;
+					else
+						autoTremolo[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x12: // pattern break
+					m.command = CMD_PATTERNBREAK; 
+					break;
+				
+				case 0x13: // auto tone portamento
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = m.param;
+				
+					tonePortaMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+				
+				case 0x14: // position jump
+					m.command = CMD_POSITIONJUMP; 
+					break;
+				
+				case 0x16: // start loop sequence
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+					
+						m.param--;
+						if(m.param < MIN(9u, loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+					}
+					
+					m.command = m.param = 0;
+					break;
+				
+				case 0x17: // play only loop nn
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+					
+						m.param--;
+						if (m.param < loopList.size())
+						{
+							if (!loopList[m.param].looped && m_nSamples < MAX_SAMPLES-1)
+								loopList[m.param].looped = ++m_nSamples;
+							m.instr = loopList[m.param].looped;
+						}
+					}
+					
+					m.command = m.param = 0;
+					break;
+				
+				case 0x18: // play sequence without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+					
+						m.param--;
+						if(m.param < MIN(9u, loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+						// switch to non-looped version of sample and create it if needed
+						if (!nonLooped[m.instr-1] && m_nSamples < MAX_SAMPLES-1)
+							nonLooped[m.instr-1] = ++m_nSamples;
+						m.instr = nonLooped[m.instr-1];
+					}
+					
+					m.command = m.param = 0;
+					break;
+					
+				case 0x19: // play only loop nn without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+					
+						m.param--;
+						if (m.param < loopList.size())
+						{
+							if (!loopList[m.param].nonLooped && m_nSamples < MAX_SAMPLES-1)
+								loopList[m.param].nonLooped = ++m_nSamples;
+							m.instr = loopList[m.param].nonLooped;
+						}
+					}
+					
+					m.command = m.param = 0;
+					break;
+				
+				case 0x1D: // fine volume slide (nibble order also swapped)
+					m.command = CMD_VOLUMESLIDE;
+					m.param = swap;
+					if(m.param & 0xF0) // slide down
+						m.param |= 0x0F;
+					else if(m.param & 0x0F)
+						m.param |= 0xF0;
+					break;
+				
+				case 0x20: // "delayed fade"
+					// just behave like either a normal fade or a notecut
+					// depending on the speed
+					if(m.param & 0xF0)
+					{
+						autoVolSlide[chn] = m.param >> 4;
+						m.command = m.param = 0;
+					} else 
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = 0xC0 | (m.param & 0xF);
+					}
+					break;
+				
+				case 0x21: // note delay
+					m.command = CMD_MODCMDEX;
+					m.param = 0xD0 | MIN(m.param, 15);
+					break;
+				
+				case 0x22: // retrigger note
+					m.command = CMD_RETRIG;
+					m.param = MIN(m.param, 15);
+					break;
+				
+				case 0x49: // set sample offset
+					m.command = CMD_OFFSET; 
+					break;
+				
+				case 0x4E: // other protracker commands (pattern loop / delay)
+					if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
+						m.command = CMD_MODCMDEX;
+					else
+						m.command = m.param = 0;
+					break;
+				
+				case 0x4F: // set speed/tempo
+					if(m.param < 0x20)
+					{
+						m.command = CMD_SPEED;
+						fileHeader.speedFrac = 0;
+					} else
+					{
+						m.command = CMD_TEMPO;
+					}
+					break;
+				
+				default:
+					m.command = m.param = 0; 
+					break;
+				}
+				
+				bool didVolSlide = false;
+				
+				// try to put volume slide in volume command
+				if(autoVolSlide[chn] && !m.volcmd)
+				{
+					if(autoVolSlide[chn] & 0xF0)
+					{
+						m.volcmd = VOLCMD_FINEVOLUP;
+						m.vol = autoVolSlide[chn] >> 4;
+					} else
+					{
+						m.volcmd = VOLCMD_FINEVOLDOWN;
+						m.vol = autoVolSlide[chn] & 0xF;
+					}
+					didVolSlide = true;
+				}
+				
+				// try to place/combine all remaining running effects.
+				if(!m.command)
+				{
+					if(autoPortaUp[chn])
+					{
+						m.command = CMD_PORTAMENTOUP;
+						m.param = autoPortaUp[chn];
+						
+					} else if(autoPortaDown[chn])
+					{
+						m.command = CMD_PORTAMENTODOWN;
+						m.param = autoPortaDown[chn];
+						
+					} else if(autoFinePorta[chn])
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = autoFinePorta[chn];
+						
+					} else if(autoTonePorta[chn])
+					{
+						m.command = CMD_TONEPORTAMENTO;
+						m.param = tonePortaMem[chn] = autoTonePorta[chn];
+						
+					} else if(autoVibrato[chn])
+					{
+						m.command = CMD_VIBRATO;
+						m.param = vibratoMem[chn] = autoVibrato[chn];
+						
+					} else if(!didVolSlide && autoVolSlide[chn])
+					{
+						m.command = CMD_VOLUMESLIDE;
+						m.param = autoVolSlide[chn];
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+						didVolSlide = true;
+						
+					} else if(autoTremolo[chn])
+					{
+						m.command = CMD_TREMOLO;
+						m.param = autoTremolo[chn];
+						
+					} else if(shouldDelay)
+					{
+						// insert a fine pattern delay here
+						m.command = CMD_S3MCMDEX;
+						m.param = 0x61;
+						shouldDelay = false;
+					
+					} else if(!didGlobalVolSlide && globalVolSlide)
+					{
+						m.command = CMD_GLOBALVOLSLIDE;
+						m.param = globalVolSlide;
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+				
+						didGlobalVolSlide = true;
+					}
+				}
+			}
+			
+			// TODO: create/use extra channels for global volslide/delay if needed
+		}
+	}
+
+	// after we know how many channels there really are...
+	m_nSamplePreAmp = 256 / m_nChannels;
+	// Setup channel pan positions and volume
+	SetupMODPanning(true);
+
+	// Skip over scripts and drumpad info
+	if(fileHeader.version > 0)
+	{
+		uint16 scriptNum;
+		uint32 length;
+		
+		while(file.CanRead(2))
+		{
+			scriptNum = file.ReadUint16BE();
+			if(scriptNum == 0xFFFF)
+				break;
+			
+			file.Skip(2);
+			length = file.ReadUint32BE();
+			file.Skip(length);
+		}
+		
+		// skip drumpad stuff
+		file.Skip(17*2);
+	}
+
+	// Reading samples
+	if(loadFlags & loadSampleData)
+	{
+		for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
+		{
+			SampleIO(
+				SampleIO::_8bit,
+				SampleIO::mono,
+				SampleIO::littleEndian,
+				SampleIO::signedPCM)
+				.ReadSample(Samples[smp], file);
+				
+			ConvertLoopSequence(Samples[smp], loopInfo[smp-1]);
+			
+			// make a non-looping duplicate of this sample if needed
+			if(nonLooped[smp-1])
+			{
+				ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp-1]], 0, Samples[smp].nLength, false);
+			}
+			
+			for(SAMPLEINDEX loop = 0; loop < loopInfo[smp-1].size(); loop++)
+			{
+				STPLoopInfo &info = loopInfo[smp-1][loop];
+			
+				// make duplicate samples for this individual section if needed
+				if(info.looped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
+				}
+				if(info.nonLooped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+OPENMPT_NAMESPACE_END
diff --git a/OpenMPT/soundlib/Snd_defs.h b/OpenMPT/soundlib/Snd_defs.h
index 687b0e8..ce5fa00 100644
--- a/OpenMPT/soundlib/Snd_defs.h
+++ b/OpenMPT/soundlib/Snd_defs.h
@@ -97,6 +97,7 @@ enum MODTYPE
 	MOD_TYPE_DIGI	= 0x8000000,
 	MOD_TYPE_UAX	= 0x10000000, // sampleset as module
 	MOD_TYPE_PLM	= 0x20000000,
+	MOD_TYPE_STP	= 0x40000000,
 };
 DECLARE_FLAGSET(MODTYPE)
 
diff --git a/OpenMPT/soundlib/Snd_fx.cpp b/OpenMPT/soundlib/Snd_fx.cpp
index 3542afb..7a482bc 100644
--- a/OpenMPT/soundlib/Snd_fx.cpp
+++ b/OpenMPT/soundlib/Snd_fx.cpp
@@ -3363,7 +3363,7 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, param, doFineSlides);
@@ -3422,7 +3422,7 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
@@ -3894,7 +3894,7 @@ void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param)
 	else
 		param = pChn->nOldVolumeSlide;
 
-	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
+	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP)))
 	{
 		// MOD / XM nibble priority
 		if((param & 0xF0) != 0)
@@ -5458,7 +5458,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio
 //------------------------------------------------------------------------------------------
 {
 	if (!period) return 0;
-	if (GetType() & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_DIGI|MOD_TYPE_MTM|MOD_TYPE_AMF0))
+	if (GetType() & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_DIGI|MOD_TYPE_MTM|MOD_TYPE_AMF0|MOD_TYPE_STP))
 	{
 		return ((3546895L * 4) << FREQ_FRACBITS) / period;
 	} else if (GetType() == MOD_TYPE_XM)
diff --git a/OpenMPT/soundlib/Sndfile.cpp b/OpenMPT/soundlib/Sndfile.cpp
index c463551..30df0c8 100644
--- a/OpenMPT/soundlib/Sndfile.cpp
+++ b/OpenMPT/soundlib/Sndfile.cpp
@@ -310,6 +310,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
 		 && !ReadJ2B(file, loadFlags)
 		 && !ReadMO3(file, loadFlags)
 		 && !ReadPT36(file, loadFlags)
+		 && !ReadSTP(file, loadFlags)
 		 && !ReadMod(file, loadFlags)
 		 && !Read669(file, loadFlags)
 		 && !ReadICE(file, loadFlags)
@@ -1043,6 +1044,8 @@ MODTYPE CSoundFile::GetBestSaveFormat() const
 	case MOD_TYPE_AMF0:
 	case MOD_TYPE_DIGI:
 		return MOD_TYPE_MOD;
+	case MOD_TYPE_STP:
+		return MOD_TYPE_XM;
 	case MOD_TYPE_MED:
 		if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments)
 		{
diff --git a/OpenMPT/soundlib/Sndfile.h b/OpenMPT/soundlib/Sndfile.h
index 5b1f4d1..c3f8dee 100644
--- a/OpenMPT/soundlib/Sndfile.h
+++ b/OpenMPT/soundlib/Sndfile.h
@@ -670,6 +670,7 @@ public:
 	bool ReadDIGI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadPLM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+	bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 
 	static std::vector<const char *> GetSupportedExtensions(bool otherFormats);
 	static mpt::Charset GetCharsetFromModType(MODTYPE modtype);
diff --git a/OpenMPT/soundlib/Tables.cpp b/OpenMPT/soundlib/Tables.cpp
index abf91a9..06250bd 100644
--- a/OpenMPT/soundlib/Tables.cpp
+++ b/OpenMPT/soundlib/Tables.cpp
@@ -87,6 +87,7 @@ static const ModFormatInfo modFormatInfo[] =
 	{ MOD_TYPE_IMF,		"Imago Orpheus",			"imf" },
 	{ MOD_TYPE_J2B,		"Galaxy Sound System",		"j2b" },
 	{ MOD_TYPE_PLM,		"Disorder Tracker 2",		"plm" },
+	{ MOD_TYPE_STP,		"Soundtracker Pro II",		"stp" },
 
 #ifndef NO_ARCHIVE_SUPPORT
 	// Compressed modules
@@ -147,6 +148,7 @@ static const ModCharsetInfo ModCharsetInfos[] =
 	{ MOD_TYPE_OKT , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DBM , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DIGI, mpt::CharsetISO8859_1  },
+	{ MOD_TYPE_STP,  mpt::CharsetISO8859_1  },
 	// Amiga // DOS
 	{ MOD_TYPE_MOD , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_MED , mpt::CharsetISO8859_1  },
stpro2_r2.patch (32,311 bytes)   
stpro2_r3.patch (33,845 bytes)   
Index: build/android_ndk/Android-minimp3-stbvorbis.mk
===================================================================
--- build/android_ndk/Android-minimp3-stbvorbis.mk	(revision 6473)
+++ build/android_ndk/Android-minimp3-stbvorbis.mk	(working copy)
@@ -75,6 +75,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: build/android_ndk/Android-mpg123-vorbis.mk
===================================================================
--- build/android_ndk/Android-mpg123-vorbis.mk	(revision 6473)
+++ build/android_ndk/Android-mpg123-vorbis.mk	(working copy)
@@ -73,6 +73,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: build/android_ndk/Android-unmo3.mk
===================================================================
--- build/android_ndk/Android-unmo3.mk	(revision 6473)
+++ build/android_ndk/Android-unmo3.mk	(working copy)
@@ -106,6 +106,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: build/android_ndk/Android.mk
===================================================================
--- build/android_ndk/Android.mk	(revision 6473)
+++ build/android_ndk/Android.mk	(working copy)
@@ -74,6 +74,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: build/autotools/Makefile.am
===================================================================
--- build/autotools/Makefile.am	(revision 6473)
+++ build/autotools/Makefile.am	(working copy)
@@ -186,6 +186,7 @@
 libopenmpt_la_SOURCES += soundlib/Load_s3m.cpp
 libopenmpt_la_SOURCES += soundlib/Load_sfx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_stm.cpp
+libopenmpt_la_SOURCES += soundlib/Load_stp.cpp
 libopenmpt_la_SOURCES += soundlib/Load_ult.cpp
 libopenmpt_la_SOURCES += soundlib/Load_umx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_wav.cpp
@@ -432,6 +433,7 @@
 libopenmpttest_SOURCES += soundlib/Load_s3m.cpp
 libopenmpttest_SOURCES += soundlib/Load_sfx.cpp
 libopenmpttest_SOURCES += soundlib/Load_stm.cpp
+libopenmpttest_SOURCES += soundlib/Load_stp.cpp
 libopenmpttest_SOURCES += soundlib/Load_ult.cpp
 libopenmpttest_SOURCES += soundlib/Load_umx.cpp
 libopenmpttest_SOURCES += soundlib/Load_wav.cpp
Index: libopenmpt/foo_openmpt.cpp
===================================================================
--- libopenmpt/foo_openmpt.cpp	(revision 6473)
+++ libopenmpt/foo_openmpt.cpp	(working copy)
@@ -319,6 +319,7 @@
 	"*.imf" ";"
 	"*.j2b" ";"
 	"*.plm" ";"
+	"*.stp" ";"
 	"*.sfx" ";"
 	"*.sfx2" ";"
 	"*.mms" ";"
Index: mptrack/Mptrack.cpp
===================================================================
--- mptrack/Mptrack.cpp	(revision 6473)
+++ mptrack/Mptrack.cpp	(working copy)
@@ -1412,7 +1412,7 @@
 		"FastTracker Modules (*.xm)|*.xm;*.xmz|"
 		"Impulse Tracker Modules (*.it)|*.it;*.itz|"
 		"OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|"
-		"Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.sfx;*.sfx2;*.mms|"
+		"Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.stp;*.sfx;*.sfx2;*.mms|"
 		"Wave Files (*.wav)|*.wav|"
 		"MIDI Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|"
 		"All Files (*.*)|*.*||")
Index: mptrack/mptrack_10.vcxproj
===================================================================
--- mptrack/mptrack_10.vcxproj	(revision 6473)
+++ mptrack/mptrack_10.vcxproj	(working copy)
@@ -785,6 +785,7 @@
     <ClCompile Include="..\soundlib\Load_digi.cpp" />
     <ClCompile Include="..\soundlib\Load_plm.cpp" />
     <ClCompile Include="..\soundlib\Load_sfx.cpp" />
+    <ClCompile Include="..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\soundlib\Message.cpp" />
     <ClCompile Include="..\soundlib\MIDIEvents.cpp" />
     <ClCompile Include="..\soundlib\MIDIMacros.cpp" />
Index: mptrack/mptrack_10.vcxproj.filters
===================================================================
--- mptrack/mptrack_10.vcxproj.filters	(revision 6473)
+++ mptrack/mptrack_10.vcxproj.filters	(working copy)
@@ -643,6 +643,9 @@
     <ClCompile Include="..\soundlib\Load_sfx.cpp">
       <Filter>Source Files\soundlib\Module Loaders</Filter>
     </ClCompile>
+    <ClCompile Include="..\soundlib\Load_stp.cpp">
+      <Filter>Source Files\soundlib\Module Loaders</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\soundlib\Loaders.h">
Index: soundlib/Load_stp.cpp
===================================================================
--- soundlib/Load_stp.cpp	(nonexistent)
+++ soundlib/Load_stp.cpp	(working copy)
@@ -0,0 +1,951 @@
+/*
+ * Load_stp.cpp
+ * ------------
+ * Purpose: STP (Soundtracker Pro II) module loader
+ * Notes  : a few exotic effects aren't supported.
+ *          multiple sample loops are supported, but only the first 10 can be used as cue points
+ *          (with 16xx and 18xx).
+ *          fractional speed values and combined auto effects are handled whenever possible,
+ *          but some effects may be omitted (and there may be tempo accuracy issues).
+ * Authors: Devin Acker
+ *          OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ *
+ * Wisdom from the Soundtracker Pro II manual:
+ * "To create shorter patterns, simply create shorter patterns."
+ */
+
+#include "stdafx.h"
+#include "Loaders.h"
+#include "Tables.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(push, 1)
+#endif
+
+// File header (except for "STP3" magic)
+struct PACKED STPFileHeader
+{
+	uint16 version;
+	uint8  numOrders;
+	uint8  patternLength;
+	uint8  orderList[128];
+	uint16 speed;
+	uint16 speedFrac;
+	uint16 timerCount;
+	uint16 flags;
+	uint32 reserved;
+	uint16 midiCount; // always 50
+	uint8  midi[50];
+	uint16 numSamples;
+	uint16 sampleStructSize;
+
+	void ConvertEndianness()
+	{
+		SwapBytesBE(version);
+		SwapBytesBE(speed);
+		SwapBytesBE(speedFrac);
+		SwapBytesBE(timerCount);
+		SwapBytesBE(flags);
+		SwapBytesBE(midiCount);
+		SwapBytesBE(numSamples);
+		SwapBytesBE(sampleStructSize);
+	}
+};
+
+STATIC_ASSERT(sizeof(STPFileHeader) == 200);
+
+
+// Sample header (versions 0 and 1)
+struct PACKED STPSampleHeaderOld
+{
+	char   pathName[31];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint32 reserved2;
+
+	void ConvertEndianness()
+	{
+		SwapBytesBE(length);
+		SwapBytesBE(loopStart);
+		SwapBytesBE(loopLength);
+		SwapBytesBE(defaultCmd);
+	}
+
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nVolume = 4u * std::min<uint16>(volume, 64);
+
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+
+	void Read(FileReader &file)
+	{
+		file.ReadConvertEndianness(*this);
+	}
+};
+
+STATIC_ASSERT(sizeof(STPSampleHeaderOld) == 82);
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(pop)
+#endif
+
+// Sample header (version 2), not packed due to variable string length and alignment in file
+struct STPSampleHeader
+{
+	//char   pathName[256];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint16 defaultPeriod;
+	uint8  finetune;
+	uint8  reserved2;
+
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nFineTune = static_cast<int8>(finetune << 3);
+		mptSmp.nVolume = 4 * MIN(volume, 64);
+
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		} else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.cues[0] = mptSmp.nLoopStart;
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+
+	void Read(FileReader &file)
+	{
+		std::string str;
+
+		file.ReadNullString(str, 255);
+		//mpt::String::Copy(pathName, str);
+
+		flags = file.ReadUint8();
+
+		file.ReadNullString(str, 29);
+		mpt::String::Copy(fileName, str);
+
+		// seek to even boundary
+		if(file.GetPosition() % 2u)
+			file.Skip(1);
+
+		length = file.ReadUint32BE();
+		volume = file.ReadUint8();
+		reserved1 = file.ReadUint8();
+
+		loopStart = file.ReadUint32BE();
+		loopLength = file.ReadUint32BE();
+
+		defaultCmd = file.ReadUint16BE();
+		defaultPeriod = file.ReadUint16BE();
+
+		finetune = file.ReadUint8();
+		reserved2 = file.ReadUint8();
+	}
+};
+
+struct STPLoopInfo
+{
+	SmpLength loopStart;
+	SmpLength loopLength;
+	SAMPLEINDEX looped;
+	SAMPLEINDEX nonLooped;
+};
+
+typedef std::vector<STPLoopInfo> STPLoopList;
+
+template <typename T>
+static void ReadSample(FileReader &file, T &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME])
+//--------------------------------------------------------------------------------------------------------------
+{
+	sampleHeader.Read(file);
+	sampleHeader.ConvertToMPT(sample);
+
+	mpt::String::Read<mpt::String::maybeNullTerminated>(sampleName, sampleHeader.fileName);
+}
+
+
+static TEMPO ConvertTempo(uint16 ciaSpeed)
+//----------------------------------------
+{
+	// 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
+	return TEMPO((125.0 * 3546.0) / ciaSpeed);
+}
+
+
+static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
+//------------------------------------------------------------------------------------------------------
+{
+	if(!src.HasSampleData()) return;
+
+	dest.FreeSample();
+
+	dest = src;
+	dest.nLength = len;
+	dest.pSample = nullptr;
+
+	if(!dest.AllocateSample())
+	{
+		return;
+	}
+
+	// only preserve cue points if the target sample length is the same
+	if(len != src.nLength)
+		MemsetZero(dest.cues);
+
+	memcpy(dest.pSample8, src.pSample8 + start, len);
+	if(loop)
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = len;
+		dest.uFlags.set(CHN_LOOP);
+	} else
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = 0;
+		dest.uFlags.reset(CHN_LOOP);
+	}
+}
+
+static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
+//--------------------------------------------------------------------
+{
+	// This should only modify a sample if it has more than one loop
+	// (otherwise, it behaves like a normal sample loop)
+	if(!smp.HasSampleData() || loopList.size() < 2) return;
+
+	ModSample newSmp = smp;
+	newSmp.nLength = 0;
+	newSmp.pSample = nullptr;
+
+	size_t numLoops = loopList.size();
+
+	// get the total length of the sample after combining all looped sections
+	for(size_t i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+
+		// if adding this loop would cause the sample length to exceed maximum,
+		// then limit and bail out
+		if((newSmp.nLength + info.loopLength > MAX_SAMPLE_LENGTH) ||
+		   (info.loopLength > MAX_SAMPLE_LENGTH) ||
+		   (info.loopStart + info.loopLength > smp.nLength))
+		{
+			numLoops = i;
+			break;
+		}
+
+		newSmp.nLength += info.loopLength;
+	}
+
+	if(!newSmp.AllocateSample())
+	{
+		return;
+	}
+
+	// start copying the looped sample data parts
+	SmpLength start = 0;
+
+	for(size_t i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+
+		memcpy(newSmp.pSample8 + start, smp.pSample8 + info.loopStart, info.loopLength);
+
+		// update loop info based on position in edited sample
+		info.loopStart = start;
+		if(i > 0 && i <= CountOf(newSmp.cues))
+		{
+			newSmp.cues[i - 1] = start;
+		}
+		start += info.loopLength;
+	}
+
+	// replace old sample with new one
+	smp.FreeSample();
+	smp = newSmp;
+
+	smp.nLoopStart = 0;
+	smp.nLoopEnd = smp.nLength;
+	smp.uFlags.set(CHN_LOOP);
+}
+
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	if(!file.ReadMagic("STP3"))
+		return false;
+
+	STPFileHeader fileHeader;
+	file.ReadConvertEndianness(fileHeader);
+	if(fileHeader.version > 2 ||
+	   fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES ||
+	   fileHeader.midiCount != 50)
+		return false;
+
+	if(loadFlags == onlyVerifyHeader)
+		return true;
+
+	InitializeGlobals(MOD_TYPE_STP);
+
+	m_nChannels = 4;
+	m_nSamples = 0;
+	m_nInstruments = 0;
+
+	m_nDefaultSpeed = fileHeader.speed;
+	m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
+
+	m_nMinPeriod = 14 * 4;
+	m_nMaxPeriod = 3424 * 4;
+
+	Order.ReadFromArray(fileHeader.orderList, fileHeader.numOrders);
+
+	std::vector<STPLoopList> loopInfo;
+	// non-looped versions of samples with loopes (when needed)
+	std::vector<SAMPLEINDEX> nonLooped;
+
+	// Load sample headers
+	SAMPLEINDEX samplesInFile = 0;
+
+	for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++)
+	{
+		// this is 1-based the same as smp
+		SAMPLEINDEX actualSmp = file.ReadUint16BE();
+		if(actualSmp >= MAX_SAMPLES)
+			return false;
+
+		samplesInFile = m_nSamples = MAX(m_nSamples, actualSmp);
+
+		ModSample &thisSmp = Samples[actualSmp];
+
+		if(fileHeader.version == 2)
+		{
+			STPSampleHeader sampleHeader;
+			uint32 headerSize = file.ReadUint32BE();
+
+			ReadSample(file, sampleHeader, thisSmp, m_szNames[actualSmp]);
+			// TODO: verify string lengths against headerSize?
+			MPT_UNUSED_VARIABLE(headerSize);
+		} else
+		{
+			STPSampleHeaderOld sampleHeaderOld;
+			ReadSample(file, sampleHeaderOld, thisSmp, m_szNames[actualSmp]);
+		}
+
+		STPLoopList loopList;
+
+		if(fileHeader.version >= 1)
+		{
+			uint16 numLoops = file.ReadUint16BE();
+
+			STPLoopInfo loop;
+			loop.looped = loop.nonLooped = 0;
+
+			if(numLoops == 0 && thisSmp.uFlags[CHN_LOOP])
+			{
+				loop.loopStart  = thisSmp.nLoopStart;
+				loop.loopLength = thisSmp.nLoopEnd - thisSmp.nLoopStart;
+				loopList.push_back(loop);
+
+			} else for(SAMPLEINDEX i = 0; i < numLoops; i++)
+			{
+				loop.loopStart  = file.ReadUint32BE();
+				loop.loopLength = file.ReadUint32BE();
+				loopList.push_back(loop);
+			}
+		}
+
+		nonLooped.resize(actualSmp);
+		loopInfo.resize(actualSmp);
+		loopInfo[actualSmp - 1] = loopList;
+	}
+
+	// Load patterns
+	uint16 numPatterns = 128;
+	if (fileHeader.version == 0)
+		numPatterns = file.ReadUint16BE();
+
+	uint16 patternLength = fileHeader.patternLength;
+	CHANNELINDEX channels = 4;
+	if(fileHeader.version > 0)
+	{
+		// Scan for total number of channels
+		FileReader::off_t patOffset = file.GetPosition();
+		for(uint16 pat = 0; pat < numPatterns; pat++)
+		{
+			PATTERNINDEX actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+			m_nChannels = std::max(m_nChannels, channels);
+
+			file.Skip(channels * patternLength * 4u);
+		}
+		file.Seek(patOffset);
+		if(m_nChannels > MAX_BASECHANNELS)
+			return false;
+	}
+
+	uint8 globalVolSlide = 0;
+	std::vector<uint8> autoFinePorta(channels, 0);
+	std::vector<uint8> autoPortaUp(channels, 0);
+	std::vector<uint8> autoPortaDown(channels, 0);
+	std::vector<uint8> autoVolSlide(channels, 0);
+	std::vector<uint8> autoVibrato(channels, 0);
+	std::vector<uint8> vibratoMem(channels, 0);
+	std::vector<uint8> autoTremolo(channels, 0);
+	std::vector<uint8> autoTonePorta(channels, 0);
+	std::vector<uint8> tonePortaMem(channels, 0);
+
+	for(uint16 pat = 0; pat < numPatterns; pat++)
+	{
+		PATTERNINDEX actualPat = pat;
+
+		if(fileHeader.version > 0)
+		{
+			actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+		}
+
+		if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
+		{
+			file.Skip(channels * patternLength * 4u);
+			continue;
+		}
+
+		for(ROWINDEX row = 0; row < patternLength; row++)
+		{
+			PatternRow rowBase = Patterns[actualPat].GetpModCommand(row, 0);
+
+			bool didGlobalVolSlide = false;
+			bool shouldDelay = false;
+
+			// if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
+			switch(fileHeader.speedFrac & 3)
+			{
+			// 1/4
+			case 1: shouldDelay = (row & 3) == 0; break;
+			// 1/2
+			case 2: shouldDelay = (row & 1) == 0; break;
+			// 3/4
+			case 3: shouldDelay = (row & 3) != 3; break;
+			}
+
+			for(CHANNELINDEX chn = 0; chn < channels; chn++)
+			{
+				ModCommand &m = rowBase[chn];
+				uint8 data[4];
+				file.ReadArray(data);
+
+				m.instr   = data[0];
+				m.note    = data[1];
+				m.command = data[2];
+				m.param   = data[3];
+
+				if(m.note)
+				{
+					m.note += 24 + NOTE_MIN;
+
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoVolSlide[chn] = 0;
+					autoVibrato[chn] = vibratoMem[chn] = 0;
+					autoTremolo[chn] = 0;
+					autoTonePorta[chn] = tonePortaMem[chn] = 0;
+				}
+
+				// this is a nibble-swapped param value used for auto fine volside
+				// and auto global fine volside
+				uint8 swap = (m.param >> 4) | (m.param << 4);
+
+				if((m.command & 0xF0) == 0xF0)
+				{
+					m.param = mpt::saturate_cast<uint8>(Util::Round(ConvertTempo(m.param | (((uint16)m.command & 0xF) << 8)).ToDouble()));
+					m.command = CMD_TEMPO;
+				} else switch(m.command)
+				{
+				case 0x00: // arpeggio
+					if(m.param)
+						m.command = CMD_ARPEGGIO;
+					break;
+
+				case 0x01: // portamento up
+					m.command = CMD_PORTAMENTOUP;
+					break;
+
+				case 0x02: // portamento down
+					m.command = CMD_PORTAMENTODOWN;
+					break;
+
+				case 0x03: // auto fine portamento up
+					autoFinePorta[chn] = 0x10 | std::min(m.param, ModCommand::PARAM(15));
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x04: // auto fine portamento down
+					autoFinePorta[chn] = 0x20 | std::min(m.param, ModCommand::PARAM(15));
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x05: // auto portamento up
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = m.param;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x06: // auto portamento down
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = m.param;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x07: // set global volume
+					m.command = CMD_GLOBALVOLUME;
+					globalVolSlide = 0;
+					break;
+
+				case 0x08: // auto global fine volume slide
+					globalVolSlide = swap;
+					m.command = m.param = 0;
+					break;
+
+				case 0x09: // fine portamento up
+					m.command = CMD_MODCMDEX;
+					m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x0A: // fine portamento down
+					m.command = CMD_MODCMDEX;
+					m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x0B: // auto fine volume slide
+					autoVolSlide[chn] = swap;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0C: // set volume
+					m.volcmd = VOLCMD_VOLUME;
+					m.vol = m.param;
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0D: // volume slide (param is swapped compared to .mod)
+					if(m.param & 0xF0)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEDOWN;
+						m.vol = m.param >> 4;
+					} else if(m.param & 0x0F)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEUP;
+						m.vol = m.param & 0xF;
+					}
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0E: // set filter (also uses opposite value compared to .mod)
+					m.command = CMD_MODCMDEX;
+					m.param = 1 ^ (m.param ? 1 : 0);
+					break;
+
+				case 0x0F: // set speed
+					m.command = CMD_SPEED;
+					fileHeader.speedFrac = m.param & 0xF;
+					m.param >>= 4;
+					break;
+
+				case 0x10: // auto vibrato
+					autoVibrato[chn] = m.param;
+					vibratoMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x11: // auto tremolo
+					if(m.param & 0xF)
+						autoTremolo[chn] = m.param;
+					else
+						autoTremolo[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x12: // pattern break
+					m.command = CMD_PATTERNBREAK;
+					break;
+
+				case 0x13: // auto tone portamento
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = m.param;
+
+					tonePortaMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x14: // position jump
+					m.command = CMD_POSITIONJUMP;
+					break;
+
+				case 0x16: // start loop sequence
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+
+						m.param--;
+						if(m.param < std::min(CountOf(ModSample().cues), loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x17: // play only loop nn
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+
+						m.param--;
+						if (m.param < loopList.size())
+						{
+							if (!loopList[m.param].looped && m_nSamples < MAX_SAMPLES - 1)
+								loopList[m.param].looped = ++m_nSamples;
+							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x18: // play sequence without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+
+						m.param--;
+						if(m.param < std::min(CountOf(ModSample().cues), loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+						// switch to non-looped version of sample and create it if needed
+						if (!nonLooped[m.instr - 1] && m_nSamples < MAX_SAMPLES - 1)
+							nonLooped[m.instr - 1] = ++m_nSamples;
+						m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x19: // play only loop nn without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList& loopList = loopInfo[m.instr-1];
+
+						m.param--;
+						if (m.param < loopList.size())
+						{
+							if (!loopList[m.param].nonLooped && m_nSamples < MAX_SAMPLES-1)
+								loopList[m.param].nonLooped = ++m_nSamples;
+							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x1D: // fine volume slide (nibble order also swapped)
+					m.command = CMD_VOLUMESLIDE;
+					m.param = swap;
+					if(m.param & 0xF0) // slide down
+						m.param |= 0x0F;
+					else if(m.param & 0x0F)
+						m.param |= 0xF0;
+					break;
+
+				case 0x20: // "delayed fade"
+					// just behave like either a normal fade or a notecut
+					// depending on the speed
+					if(m.param & 0xF0)
+					{
+						autoVolSlide[chn] = m.param >> 4;
+						m.command = m.param = 0;
+					} else
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = 0xC0 | (m.param & 0xF);
+					}
+					break;
+
+				case 0x21: // note delay
+					m.command = CMD_MODCMDEX;
+					m.param = 0xD0 | MIN(m.param, 15);
+					break;
+
+				case 0x22: // retrigger note
+					m.command = CMD_RETRIG;
+					m.param = MIN(m.param, 15);
+					break;
+
+				case 0x49: // set sample offset
+					m.command = CMD_OFFSET;
+					break;
+
+				case 0x4E: // other protracker commands (pattern loop / delay)
+					if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
+						m.command = CMD_MODCMDEX;
+					else
+						m.command = m.param = 0;
+					break;
+
+				case 0x4F: // set speed/tempo
+					if(m.param < 0x20)
+					{
+						m.command = CMD_SPEED;
+						fileHeader.speedFrac = 0;
+					} else
+					{
+						m.command = CMD_TEMPO;
+					}
+					break;
+
+				default:
+					m.command = CMD_NONE;
+					break;
+				}
+
+				bool didVolSlide = false;
+
+				// try to put volume slide in volume command
+				if(autoVolSlide[chn] && !m.volcmd)
+				{
+					if(autoVolSlide[chn] & 0xF0)
+					{
+						m.volcmd = VOLCMD_FINEVOLUP;
+						m.vol = autoVolSlide[chn] >> 4;
+					} else
+					{
+						m.volcmd = VOLCMD_FINEVOLDOWN;
+						m.vol = autoVolSlide[chn] & 0xF;
+					}
+					didVolSlide = true;
+				}
+
+				// try to place/combine all remaining running effects.
+				if(!m.command)
+				{
+					if(autoPortaUp[chn])
+					{
+						m.command = CMD_PORTAMENTOUP;
+						m.param = autoPortaUp[chn];
+
+					} else if(autoPortaDown[chn])
+					{
+						m.command = CMD_PORTAMENTODOWN;
+						m.param = autoPortaDown[chn];
+
+					} else if(autoFinePorta[chn])
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = autoFinePorta[chn];
+
+					} else if(autoTonePorta[chn])
+					{
+						m.command = CMD_TONEPORTAMENTO;
+						m.param = tonePortaMem[chn] = autoTonePorta[chn];
+
+					} else if(autoVibrato[chn])
+					{
+						m.command = CMD_VIBRATO;
+						m.param = vibratoMem[chn] = autoVibrato[chn];
+
+					} else if(!didVolSlide && autoVolSlide[chn])
+					{
+						m.command = CMD_VOLUMESLIDE;
+						m.param = autoVolSlide[chn];
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+						didVolSlide = true;
+
+					} else if(autoTremolo[chn])
+					{
+						m.command = CMD_TREMOLO;
+						m.param = autoTremolo[chn];
+
+					} else if(shouldDelay)
+					{
+						// insert a fine pattern delay here
+						m.command = CMD_S3MCMDEX;
+						m.param = 0x61;
+						shouldDelay = false;
+
+					} else if(!didGlobalVolSlide && globalVolSlide)
+					{
+						m.command = CMD_GLOBALVOLSLIDE;
+						m.param = globalVolSlide;
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+
+						didGlobalVolSlide = true;
+					}
+				}
+			}
+
+			// TODO: create/use extra channels for global volslide/delay if needed
+		}
+	}
+
+	// after we know how many channels there really are...
+	m_nSamplePreAmp = 256 / m_nChannels;
+	// Setup channel pan positions and volume
+	SetupMODPanning(true);
+
+	// Skip over scripts and drumpad info
+	if(fileHeader.version > 0)
+	{
+		uint16 scriptNum;
+		uint32 length;
+
+		while(file.CanRead(2))
+		{
+			scriptNum = file.ReadUint16BE();
+			if(scriptNum == 0xFFFF)
+				break;
+
+			file.Skip(2);
+			length = file.ReadUint32BE();
+			file.Skip(length);
+		}
+
+		// skip drumpad stuff
+		file.Skip(17*2);
+	}
+
+	// Reading samples
+	if(loadFlags & loadSampleData)
+	{
+		for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
+		{
+			SampleIO(
+				SampleIO::_8bit,
+				SampleIO::mono,
+				SampleIO::littleEndian,
+				SampleIO::signedPCM)
+				.ReadSample(Samples[smp], file);
+
+			ConvertLoopSequence(Samples[smp], loopInfo[smp-1]);
+
+			// make a non-looping duplicate of this sample if needed
+			if(nonLooped[smp-1])
+			{
+				ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp-1]], 0, Samples[smp].nLength, false);
+			}
+
+			for(SAMPLEINDEX loop = 0; loop < loopInfo[smp-1].size(); loop++)
+			{
+				STPLoopInfo &info = loopInfo[smp-1][loop];
+
+				// make duplicate samples for this individual section if needed
+				if(info.looped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
+				}
+				if(info.nonLooped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+OPENMPT_NAMESPACE_END

Property changes on: soundlib/Load_stp.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/x-c++src
\ No newline at end of property
Index: soundlib/Snd_defs.h
===================================================================
--- soundlib/Snd_defs.h	(revision 6473)
+++ soundlib/Snd_defs.h	(working copy)
@@ -98,6 +98,7 @@
 	MOD_TYPE_UAX	= 0x10000000, // sampleset as module
 	MOD_TYPE_PLM	= 0x20000000,
 	MOD_TYPE_SFX	= 0x40000000,
+	MOD_TYPE_STP	= 0x80000000,
 };
 DECLARE_FLAGSET(MODTYPE)
 
Index: soundlib/Snd_fx.cpp
===================================================================
--- soundlib/Snd_fx.cpp	(revision 6473)
+++ soundlib/Snd_fx.cpp	(working copy)
@@ -3358,7 +3358,7 @@
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, param, doFineSlides);
@@ -3417,7 +3417,7 @@
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
@@ -3895,7 +3895,7 @@
 	else
 		param = pChn->nOldVolumeSlide;
 
-	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
+	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP)))
 	{
 		// MOD / XM nibble priority
 		if((param & 0xF0) != 0)
@@ -5468,7 +5468,7 @@
 //------------------------------------------------------------------------------------------
 {
 	if (!period) return 0;
-	if (GetType() & (MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_MTM | MOD_TYPE_AMF0 | MOD_TYPE_SFX))
+	if (GetType() & (MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_MTM | MOD_TYPE_AMF0 | MOD_TYPE_SFX | MOD_TYPE_STP))
 	{
 		return ((3546895L * 4) << FREQ_FRACBITS) / period;
 	} else if (GetType() == MOD_TYPE_XM)
Index: soundlib/Sndfile.cpp
===================================================================
--- soundlib/Sndfile.cpp	(revision 6473)
+++ soundlib/Sndfile.cpp	(working copy)
@@ -312,6 +312,7 @@
 			 && !ReadJ2B(file, loadFlags)
 			 && !ReadPT36(file, loadFlags)
 			 && !ReadSFX(file, loadFlags)
+			 && !ReadSTP(file, loadFlags)
 			 && !ReadMod(file, loadFlags)
 			 && !ReadICE(file, loadFlags)
 			 && !Read669(file, loadFlags)
@@ -1058,6 +1059,7 @@
 	case MOD_TYPE_AMF0:
 	case MOD_TYPE_DIGI:
 	case MOD_TYPE_SFX:
+	case MOD_TYPE_STP:
 		return MOD_TYPE_MOD;
 	case MOD_TYPE_MED:
 		if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments)
Index: soundlib/Sndfile.h
===================================================================
--- soundlib/Sndfile.h	(revision 6473)
+++ soundlib/Sndfile.h	(working copy)
@@ -668,6 +668,7 @@
 	bool ReadPLM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadSFX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+	bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 
 	static std::vector<const char *> GetSupportedExtensions(bool otherFormats);
 	static mpt::Charset GetCharsetFromModType(MODTYPE modtype);
Index: soundlib/Tables.cpp
===================================================================
--- soundlib/Tables.cpp	(revision 6473)
+++ soundlib/Tables.cpp	(working copy)
@@ -90,6 +90,7 @@
 	{ MOD_TYPE_SFX,		"SoundFX",					"sfx" },
 	{ MOD_TYPE_SFX,		"SoundFX",					"sfx2" },
 	{ MOD_TYPE_SFX,		"MultiMedia Sound",			"mms" },
+	{ MOD_TYPE_STP,		"Soundtracker Pro II",		"stp" },
 
 #ifndef NO_ARCHIVE_SUPPORT
 	// Compressed modules
@@ -151,6 +152,7 @@
 	{ MOD_TYPE_DBM , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DIGI, mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_SFX , mpt::CharsetISO8859_1  },
+	{ MOD_TYPE_STP,  mpt::CharsetISO8859_1  },
 	// Amiga // DOS
 	{ MOD_TYPE_MOD , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_MED , mpt::CharsetISO8859_1  },
stpro2_r3.patch (33,845 bytes)   
stpro2_r4.patch (43,295 bytes)   
Index: android_ndk/Android.mk
===================================================================
--- android_ndk/Android.mk	(revision 6938)
+++ android_ndk/Android.mk	(working copy)
@@ -187,6 +187,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: autotools/Makefile.am
===================================================================
--- autotools/Makefile.am	(revision 6938)
+++ autotools/Makefile.am	(working copy)
@@ -194,6 +194,7 @@
 libopenmpt_la_SOURCES += soundlib/Load_s3m.cpp
 libopenmpt_la_SOURCES += soundlib/Load_sfx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_stm.cpp
+libopenmpt_la_SOURCES += soundlib/Load_stp.cpp
 libopenmpt_la_SOURCES += soundlib/Load_ult.cpp
 libopenmpt_la_SOURCES += soundlib/Load_umx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_wav.cpp
@@ -444,6 +445,7 @@
 libopenmpttest_SOURCES += soundlib/Load_s3m.cpp
 libopenmpttest_SOURCES += soundlib/Load_sfx.cpp
 libopenmpttest_SOURCES += soundlib/Load_stm.cpp
+libopenmpttest_SOURCES += soundlib/Load_stp.cpp
 libopenmpttest_SOURCES += soundlib/Load_ult.cpp
 libopenmpttest_SOURCES += soundlib/Load_umx.cpp
 libopenmpttest_SOURCES += soundlib/Load_wav.cpp
Index: vs2008/libopenmpt-small.vcproj
===================================================================
--- vs2008/libopenmpt-small.vcproj	(revision 6938)
+++ vs2008/libopenmpt-small.vcproj	(working copy)
@@ -1075,6 +1075,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008/libopenmpt.vcproj
===================================================================
--- vs2008/libopenmpt.vcproj	(revision 6938)
+++ vs2008/libopenmpt.vcproj	(working copy)
@@ -1079,6 +1079,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008/libopenmpt_test.vcproj
===================================================================
--- vs2008/libopenmpt_test.vcproj	(revision 6938)
+++ vs2008/libopenmpt_test.vcproj	(working copy)
@@ -807,6 +807,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008/OpenMPT.vcproj
===================================================================
--- vs2008/OpenMPT.vcproj	(revision 6938)
+++ vs2008/OpenMPT.vcproj	(working copy)
@@ -2553,6 +2553,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008w2k/libopenmpt-small.vcproj
===================================================================
--- vs2008w2k/libopenmpt-small.vcproj	(revision 6938)
+++ vs2008w2k/libopenmpt-small.vcproj	(working copy)
@@ -1075,6 +1075,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008w2k/libopenmpt.vcproj
===================================================================
--- vs2008w2k/libopenmpt.vcproj	(revision 6938)
+++ vs2008w2k/libopenmpt.vcproj	(working copy)
@@ -1079,6 +1079,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008w2k/libopenmpt_test.vcproj
===================================================================
--- vs2008w2k/libopenmpt_test.vcproj	(revision 6938)
+++ vs2008w2k/libopenmpt_test.vcproj	(working copy)
@@ -807,6 +807,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2008w2k/OpenMPT.vcproj
===================================================================
--- vs2008w2k/OpenMPT.vcproj	(revision 6938)
+++ vs2008w2k/OpenMPT.vcproj	(working copy)
@@ -2553,6 +2553,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\soundlib\Load_stp.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\soundlib\Load_ult.cpp"
 				>
 			</File>
Index: vs2010/libopenmpt-small.vcxproj
===================================================================
--- vs2010/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2010/libopenmpt-small.vcxproj	(working copy)
@@ -510,6 +510,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2010/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2010/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010/libopenmpt.vcxproj
===================================================================
--- vs2010/libopenmpt.vcxproj	(revision 6938)
+++ vs2010/libopenmpt.vcxproj	(working copy)
@@ -510,6 +510,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010/libopenmpt.vcxproj.filters
===================================================================
--- vs2010/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2010/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010/libopenmpt_test.vcxproj
===================================================================
--- vs2010/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2010/libopenmpt_test.vcxproj	(working copy)
@@ -358,6 +358,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2010/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2010/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010/OpenMPT.vcxproj
===================================================================
--- vs2010/OpenMPT.vcxproj	(revision 6938)
+++ vs2010/OpenMPT.vcxproj	(working copy)
@@ -1047,6 +1047,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010/OpenMPT.vcxproj.filters
===================================================================
--- vs2010/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2010/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010xp/libopenmpt-small.vcxproj
===================================================================
--- vs2010xp/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2010xp/libopenmpt-small.vcxproj	(working copy)
@@ -508,6 +508,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010xp/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2010xp/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2010xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010xp/libopenmpt.vcxproj
===================================================================
--- vs2010xp/libopenmpt.vcxproj	(revision 6938)
+++ vs2010xp/libopenmpt.vcxproj	(working copy)
@@ -508,6 +508,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010xp/libopenmpt.vcxproj.filters
===================================================================
--- vs2010xp/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2010xp/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010xp/libopenmpt_test.vcxproj
===================================================================
--- vs2010xp/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2010xp/libopenmpt_test.vcxproj	(working copy)
@@ -357,6 +357,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010xp/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2010xp/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2010xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2010xp/OpenMPT.vcxproj
===================================================================
--- vs2010xp/OpenMPT.vcxproj	(revision 6938)
+++ vs2010xp/OpenMPT.vcxproj	(working copy)
@@ -1044,6 +1044,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2010xp/OpenMPT.vcxproj.filters
===================================================================
--- vs2010xp/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2010xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012/libopenmpt-small.vcxproj
===================================================================
--- vs2012/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2012/libopenmpt-small.vcxproj	(working copy)
@@ -518,6 +518,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2012/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2012/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012/libopenmpt.vcxproj
===================================================================
--- vs2012/libopenmpt.vcxproj	(revision 6938)
+++ vs2012/libopenmpt.vcxproj	(working copy)
@@ -518,6 +518,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012/libopenmpt.vcxproj.filters
===================================================================
--- vs2012/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2012/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012/libopenmpt_test.vcxproj
===================================================================
--- vs2012/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2012/libopenmpt_test.vcxproj	(working copy)
@@ -362,6 +362,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2012/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2012/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012/OpenMPT.vcxproj
===================================================================
--- vs2012/OpenMPT.vcxproj	(revision 6938)
+++ vs2012/OpenMPT.vcxproj	(working copy)
@@ -1059,6 +1059,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012/OpenMPT.vcxproj.filters
===================================================================
--- vs2012/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2012/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012xp/libopenmpt-small.vcxproj
===================================================================
--- vs2012xp/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2012xp/libopenmpt-small.vcxproj	(working copy)
@@ -518,6 +518,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012xp/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2012xp/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2012xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012xp/libopenmpt.vcxproj
===================================================================
--- vs2012xp/libopenmpt.vcxproj	(revision 6938)
+++ vs2012xp/libopenmpt.vcxproj	(working copy)
@@ -518,6 +518,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012xp/libopenmpt.vcxproj.filters
===================================================================
--- vs2012xp/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2012xp/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012xp/libopenmpt_test.vcxproj
===================================================================
--- vs2012xp/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2012xp/libopenmpt_test.vcxproj	(working copy)
@@ -362,6 +362,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012xp/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2012xp/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2012xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2012xp/OpenMPT.vcxproj
===================================================================
--- vs2012xp/OpenMPT.vcxproj	(revision 6938)
+++ vs2012xp/OpenMPT.vcxproj	(working copy)
@@ -1059,6 +1059,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2012xp/OpenMPT.vcxproj.filters
===================================================================
--- vs2012xp/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2012xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013/libopenmpt-small.vcxproj
===================================================================
--- vs2013/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2013/libopenmpt-small.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2013/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2013/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013/libopenmpt.vcxproj
===================================================================
--- vs2013/libopenmpt.vcxproj	(revision 6938)
+++ vs2013/libopenmpt.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013/libopenmpt.vcxproj.filters
===================================================================
--- vs2013/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2013/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013/libopenmpt_test.vcxproj
===================================================================
--- vs2013/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2013/libopenmpt_test.vcxproj	(working copy)
@@ -363,6 +363,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2013/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2013/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013/OpenMPT.vcxproj
===================================================================
--- vs2013/OpenMPT.vcxproj	(revision 6938)
+++ vs2013/OpenMPT.vcxproj	(working copy)
@@ -1060,6 +1060,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013/OpenMPT.vcxproj.filters
===================================================================
--- vs2013/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2013/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013xp/libopenmpt-small.vcxproj
===================================================================
--- vs2013xp/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2013xp/libopenmpt-small.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013xp/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2013xp/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2013xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013xp/libopenmpt.vcxproj
===================================================================
--- vs2013xp/libopenmpt.vcxproj	(revision 6938)
+++ vs2013xp/libopenmpt.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013xp/libopenmpt.vcxproj.filters
===================================================================
--- vs2013xp/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2013xp/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013xp/libopenmpt_test.vcxproj
===================================================================
--- vs2013xp/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2013xp/libopenmpt_test.vcxproj	(working copy)
@@ -363,6 +363,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013xp/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2013xp/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2013xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2013xp/OpenMPT.vcxproj
===================================================================
--- vs2013xp/OpenMPT.vcxproj	(revision 6938)
+++ vs2013xp/OpenMPT.vcxproj	(working copy)
@@ -1060,6 +1060,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2013xp/OpenMPT.vcxproj.filters
===================================================================
--- vs2013xp/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2013xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1160,6 +1160,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015/libopenmpt-small.vcxproj
===================================================================
--- vs2015/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2015/libopenmpt-small.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2015/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2015/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015/libopenmpt.vcxproj
===================================================================
--- vs2015/libopenmpt.vcxproj	(revision 6938)
+++ vs2015/libopenmpt.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015/libopenmpt.vcxproj.filters
===================================================================
--- vs2015/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2015/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015/libopenmpt_test.vcxproj
===================================================================
--- vs2015/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2015/libopenmpt_test.vcxproj	(working copy)
@@ -363,6 +363,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2015/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2015/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015/OpenMPT.vcxproj
===================================================================
--- vs2015/OpenMPT.vcxproj	(revision 6938)
+++ vs2015/OpenMPT.vcxproj	(working copy)
@@ -1060,6 +1060,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015/OpenMPT.vcxproj.filters
===================================================================
--- vs2015/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2015/OpenMPT.vcxproj.filters	(working copy)
@@ -1169,6 +1169,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015xp/libopenmpt-small.vcxproj
===================================================================
--- vs2015xp/libopenmpt-small.vcxproj	(revision 6938)
+++ vs2015xp/libopenmpt-small.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015xp/libopenmpt-small.vcxproj.filters
===================================================================
--- vs2015xp/libopenmpt-small.vcxproj.filters	(revision 6938)
+++ vs2015xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015xp/libopenmpt.vcxproj
===================================================================
--- vs2015xp/libopenmpt.vcxproj	(revision 6938)
+++ vs2015xp/libopenmpt.vcxproj	(working copy)
@@ -519,6 +519,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015xp/libopenmpt.vcxproj.filters
===================================================================
--- vs2015xp/libopenmpt.vcxproj.filters	(revision 6938)
+++ vs2015xp/libopenmpt.vcxproj.filters	(working copy)
@@ -503,6 +503,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015xp/libopenmpt_test.vcxproj
===================================================================
--- vs2015xp/libopenmpt_test.vcxproj	(revision 6938)
+++ vs2015xp/libopenmpt_test.vcxproj	(working copy)
@@ -363,6 +363,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015xp/libopenmpt_test.vcxproj.filters
===================================================================
--- vs2015xp/libopenmpt_test.vcxproj.filters	(revision 6938)
+++ vs2015xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -521,6 +521,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: vs2015xp/OpenMPT.vcxproj
===================================================================
--- vs2015xp/OpenMPT.vcxproj	(revision 6938)
+++ vs2015xp/OpenMPT.vcxproj	(working copy)
@@ -1060,6 +1060,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: vs2015xp/OpenMPT.vcxproj.filters
===================================================================
--- vs2015xp/OpenMPT.vcxproj.filters	(revision 6938)
+++ vs2015xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1169,6 +1169,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
stpro2_r4.patch (43,295 bytes)   
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Activities

manx

manx

2016-03-05 09:06

administrator   ~0002274

A few comments (I did not check the whole patch yet and I'm not the expert on various file formats):

  1. #pragma pack(pop) should be moved above the struct STPSampleHeader.
  2. Please do not allocate various structs as static because this will break when multiple threads load files via libopenmpt. Always allocate temporary storage either on the stack or if it is too large for the stack on the heap (i.e. just use "STPSampleHeader sampleHeader;" instead of "static STPSampleHeader sampleHeader;" here (and the other places accordingly)).
  3. Totally minor and trivial to do when merging the patch: You did not add Load_stp.cpp to the various build systems. The Makefile will automatically pick it put, build/android_ndk/Android*.mk, build/autotools/Makefile.am (twice!) need manual work, MSVC build system needs to be regenerated with build/regenerade_vs_projects.cmd (copy premake either by hand into include/premake/bin/release/premake5.exe or, if you have Visual Studio, PowerShell (Win7 or later) and 7-zip, via build/download_externals.cmd (use with care though)). If you are not on Windows, not updating the MSVC projects is no big deal though (would just make the patch rather unreadable anyway because of the many different project files), as we can always trivially fix that up.
  4. I think in CSoundFile::Create, ReadSTP should be moved further up, but I'm not sure to where exactly. Saga Musix will have a more qualified opinion on that.
Revenant

Revenant

2016-03-05 09:37

reporter   ~0002275

  1. Done
  2. Sorry, that was done out of habit. I'm usually wary of putting large-ish structs on the stack but I guess in this case it's not a big deal unless you think otherwise
  3. Done (for android and autotools)
  4. That's fine, I just put it at the end with the thought that it's probably one of the least common available formats and thus doesn't really need to be attempted until more common formats have been tried already.

I'll attach a newer patch later, pending any other feedback you guys have.

Saga Musix

Saga Musix

2016-03-05 10:30

administrator   ~0002276

Regarding 4, it's a mixture between "most used" and "how sane are the magic bytes". For example, M15 needs to be the last because it has no magic bytes at all, and 669 should be one of the last because its magic bytes are "if" (which could conflict with song titles in various module formats).

Saga Musix

Saga Musix

2016-03-13 16:32

administrator   ~0002283

Last edited: 2016-03-13 16:32

Do you know how relevant the missing features are for most modules? It's always nice to add support for new formats of course, but I would like to not repeat errors of the past and add support for tons of only partially-supported formats when there are already good players out there. For example, would there be many modules that require multiple sample loops? Can this feature maybe be faked using sustain loops or duplicating sample slots?

Revenant

Revenant

2016-03-13 18:44

reporter   ~0002284

It's hard to say, since there don't seem to be a lot of publicly-available modules in the format to begin with and none of the few available on Modland use any "weird" features other than fractional speed values.

Faking some of the features like multiple loops would probably be doable but I think I'd have to make up some example modules myself just to see exactly how the tracker itself handles them.

Revenant

Revenant

2016-03-14 06:08

reporter   ~0002285

Ok, after actually checking out the feature in the tracker, here's a basic summary.

If a sample has just one loop, it's handled as normal; otherwise, all the looped sections are played one after another, starting from the first loop's start point instead of the beginning of the sample (basically, automatic beat slicing).

Normally each looped section is played once at a time, in order, returning to the first one after playing the final one. This can be faked easily by just messing with the sample data at load time. The not-so-easy part is the effects, which make different behavior:

16xx: start sample at loop-start number xx (can easily be done with "set offset" command but probably with some loss of precision)
17xx: Play loop number xx only
18xx: same as 16xx but doesn't go back to first loop after playing the last one
19xx: same as 17xx but plays the selected loop only one time

(The params are decimal numbers 01-99, out of range values (including 00) are the same as using 01)

Effects 17/18/19 could be handled by just making duplicate samples as needed, although the implementation might get a little hairy.

I'd love to be able to get my hands on some modules that actually use this feature (or any other modules in this format at all, really), but for now all I have is the very small/simple selection on Modland. I might try to contact the author of the tracker at some point in search of some more, though.

(On that note, there's also of course a Soundtracker Pro 1, which seems to essentially be Ultimate Soundtracker with the same fractional-speed feature and variable pattern lengths. But I can't find any modules for it online and I can't get it to recognize keyboard input in WinUAE, so I doubt I'll bother trying to handle that one...)

Saga Musix

Saga Musix

2016-03-14 12:25

administrator   ~0002286

16xx: start sample at loop-start number xx (can easily be done with "set offset" command but probably with some loss of precision)

The MPTM volume command VOLCMD_OFFSET can be used for that, as you get 9 freely definable offset positions.

Given the number of trackers in the 90s, it's very much possible that ModLand's representation is accurate that noone just ever used the feature.

Revenant

Revenant

2016-03-14 16:53

reporter   ~0002287

Yeah, unfortunately I've had pretty much the same assumption. It's a pretty interesting and fairly sophisticated tracker, but I guess by 1996 anyone interested in the concept of a "pro" Amiga tracker was probably already using OctaMED instead.

Anyway, if you're still interested in actually supporting the format, I can at least add basic loop sequence support and the 16xx effect and post a new patch.

Saga Musix

Saga Musix

2016-03-14 16:55

administrator   ~0002288

Sure, go ahead. :)
I'm a bit busy recently so I haven't reviewed and incorporated your patches yet, but I will hopefully find the time to do so soon. There are some things I potentially want to modify since the number of MODTYPE* flags is getting a bit large while it's technically not needed to have a separate flag for all formats, but I'm not sure how to tackle that yet.

Revenant

Revenant

2016-03-31 06:36

reporter   ~0002314

Uploaded a second patch which handles all four of the "loop sequence" effects using cue points and sample duplication. I also fixed some other effect behavior (auto volume slide / auto global volume slide should be treated as fine slides, not normal ones).

Crappy loop test file that I made in about five minutes:
https://dl.dropboxusercontent.com/u/43107309/looptest.stp
(contains two identical samples with different loop sequences)

There are probably still some weird effect quirks that I'm totally unaware of, but...

Saga Musix

Saga Musix

2016-04-30 13:18

administrator   ~0002356

Last edited: 2016-04-30 13:25

I've now finally started looking into this loader a bit more, and it's a bit brittle, so I'm not sure if it will make it into the next release (it will require a big review). Problems fixed so far:

  • Hardcoded array sizes (CountOf(ModSample().cues) should be used rather than 9)
  • Setting null terminator for wrong string due to copy&paste errors (this is why there is mpt::String::Copy, which is an improved version of str(n)cpy). Otherwise, there is also the templated mpt::String::SetNullTerminator which will always set the null terminator correctly (see also previous point - hardcoded array sized).
  • m_nChannels count MUST NOT change after a pattern has been allocated, otherwise all previously allocated patterns will be interpreted incorrectly.
  • Minor style issue: if(dest.AllocateSample() != len) dest.FreeSample(); - this is not necessary, since allocating a sample will either return a value as big as the requested size or fail completely (return 0), in which case there is nothing to free. ;)
  • Checks against MAX_SAMPLES are always a bit unintuitive since it's the array size, as such m_nSamples == MAX_SAMPLES is an overflow. I would like to somehow fix this in the future but until then, use this with care.

I hope this list helps with implementing future loaders. ;)

By now it might make sense to introduce auto-slide commands in OpenMPT since they are used in several formats.

Saga Musix

Saga Musix

2016-04-30 13:34

administrator   ~0002357

I'm also not quite sure if this module plays as intended - see samples 10 and 11:
http://files.exotica.org.uk/modland/?file=pub/modules/SoundTracker%20Pro%20II/-%20unknown/jungle%20in%20germany.stp

Saga Musix

Saga Musix

2016-06-08 23:51

administrator   ~0002446

I've attached the current status of the code to this issue (stpro2_r3.patch). I haven't had a look at it in a while now, but I'm still hesistant to add it in its current shape. Given the whopping amount of five modules on ModLand, I guess it's not too urgent to support this format.

Saga Musix

Saga Musix

2016-08-20 12:35

administrator   ~0002621

No functional changes, but I updated the patch to make use of the recent endianness-related code changes.

johan.bakker

johan.bakker

2016-08-22 17:58

reporter   ~0002624

Snip<

I'd like to be able to find more files to test with than the very few on Modland

I've uploaded some mod files, see link bellow.

https://drive.google.com/file/d/0B1qzOm-C-64iZ1ktLUd4aHM2UXM/view?usp=sharing

Thanks for all the effort!

Saga Musix

Saga Musix

2017-02-07 17:51

administrator   ~0002865

Hey Revenant, I hope you're still around. I am now finally ready to merge this patch after a few remaining modifications.
Currently your code crashes on mod.acid sunday 2 found in the package provided by Johan, because one of the samples has actualSmp index of 0. Since you have more experience with using STPro, could you maybe check how that sample is supposed to be treated? Should we just ignore this sample, or it is actually possible to have a sample index of 0 (in which case we would have to shift all indices by 1)?

Saga Musix

Saga Musix

2017-02-07 18:37

administrator   ~0002866

For the reference, here's the current state of the loader.

stpro2_r5.patch (70,513 bytes)   
Index: build/android_ndk/Android.mk
===================================================================
--- build/android_ndk/Android.mk	(revision 7555)
+++ build/android_ndk/Android.mk	(working copy)
@@ -187,6 +187,7 @@
 	soundlib/Load_s3m.cpp \
 	soundlib/Load_sfx.cpp \
 	soundlib/Load_stm.cpp \
+	soundlib/Load_stp.cpp \
 	soundlib/Load_ult.cpp \
 	soundlib/Load_umx.cpp \
 	soundlib/Load_wav.cpp \
Index: build/autotools/Makefile.am
===================================================================
--- build/autotools/Makefile.am	(revision 7555)
+++ build/autotools/Makefile.am	(working copy)
@@ -205,6 +205,7 @@
 libopenmpt_la_SOURCES += soundlib/Load_s3m.cpp
 libopenmpt_la_SOURCES += soundlib/Load_sfx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_stm.cpp
+libopenmpt_la_SOURCES += soundlib/Load_stp.cpp
 libopenmpt_la_SOURCES += soundlib/Load_ult.cpp
 libopenmpt_la_SOURCES += soundlib/Load_umx.cpp
 libopenmpt_la_SOURCES += soundlib/Load_wav.cpp
@@ -463,6 +464,7 @@
 libopenmpttest_SOURCES += soundlib/Load_s3m.cpp
 libopenmpttest_SOURCES += soundlib/Load_sfx.cpp
 libopenmpttest_SOURCES += soundlib/Load_stm.cpp
+libopenmpttest_SOURCES += soundlib/Load_stp.cpp
 libopenmpttest_SOURCES += soundlib/Load_ult.cpp
 libopenmpttest_SOURCES += soundlib/Load_umx.cpp
 libopenmpttest_SOURCES += soundlib/Load_wav.cpp
Index: build/vs2010/libopenmpt-small.vcxproj
===================================================================
--- build/vs2010/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2010/libopenmpt-small.vcxproj	(working copy)
@@ -529,6 +529,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2010/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2010/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2010/libopenmpt.vcxproj
===================================================================
--- build/vs2010/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2010/libopenmpt.vcxproj	(working copy)
@@ -529,6 +529,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2010/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2010/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2010/libopenmpt_test.vcxproj
===================================================================
--- build/vs2010/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2010/libopenmpt_test.vcxproj	(working copy)
@@ -361,6 +361,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2010/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2010/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2010xp/libopenmpt-small.vcxproj
===================================================================
--- build/vs2010xp/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2010xp/libopenmpt-small.vcxproj	(working copy)
@@ -527,6 +527,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010xp/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2010xp/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2010xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2010xp/libopenmpt.vcxproj
===================================================================
--- build/vs2010xp/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2010xp/libopenmpt.vcxproj	(working copy)
@@ -527,6 +527,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010xp/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2010xp/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2010xp/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2010xp/libopenmpt_test.vcxproj
===================================================================
--- build/vs2010xp/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2010xp/libopenmpt_test.vcxproj	(working copy)
@@ -360,6 +360,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2010xp/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2010xp/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2010xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012/libopenmpt-small.vcxproj
===================================================================
--- build/vs2012/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2012/libopenmpt-small.vcxproj	(working copy)
@@ -537,6 +537,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2012/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2012/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012/libopenmpt.vcxproj
===================================================================
--- build/vs2012/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2012/libopenmpt.vcxproj	(working copy)
@@ -537,6 +537,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2012/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2012/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012/libopenmpt_test.vcxproj
===================================================================
--- build/vs2012/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2012/libopenmpt_test.vcxproj	(working copy)
@@ -365,6 +365,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2012/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2012/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012xp/libopenmpt-small.vcxproj
===================================================================
--- build/vs2012xp/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2012xp/libopenmpt-small.vcxproj	(working copy)
@@ -537,6 +537,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012xp/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2012xp/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2012xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012xp/libopenmpt.vcxproj
===================================================================
--- build/vs2012xp/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2012xp/libopenmpt.vcxproj	(working copy)
@@ -537,6 +537,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012xp/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2012xp/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2012xp/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2012xp/libopenmpt_test.vcxproj
===================================================================
--- build/vs2012xp/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2012xp/libopenmpt_test.vcxproj	(working copy)
@@ -365,6 +365,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2012xp/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2012xp/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2012xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013/libopenmpt-small.vcxproj
===================================================================
--- build/vs2013/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2013/libopenmpt-small.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2013/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2013/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013/libopenmpt.vcxproj
===================================================================
--- build/vs2013/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2013/libopenmpt.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2013/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2013/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013/libopenmpt_test.vcxproj
===================================================================
--- build/vs2013/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2013/libopenmpt_test.vcxproj	(working copy)
@@ -366,6 +366,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2013/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2013/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013/OpenMPT.vcxproj
===================================================================
--- build/vs2013/OpenMPT.vcxproj	(revision 7555)
+++ build/vs2013/OpenMPT.vcxproj	(working copy)
@@ -1062,6 +1062,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2013/OpenMPT.vcxproj.filters	(revision 7555)
+++ build/vs2013/OpenMPT.vcxproj.filters	(working copy)
@@ -1169,6 +1169,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013xp/libopenmpt-small.vcxproj
===================================================================
--- build/vs2013xp/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2013xp/libopenmpt-small.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013xp/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2013xp/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2013xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013xp/libopenmpt.vcxproj
===================================================================
--- build/vs2013xp/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2013xp/libopenmpt.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013xp/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2013xp/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2013xp/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013xp/libopenmpt_test.vcxproj
===================================================================
--- build/vs2013xp/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2013xp/libopenmpt_test.vcxproj	(working copy)
@@ -366,6 +366,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013xp/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2013xp/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2013xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2013xp/OpenMPT.vcxproj
===================================================================
--- build/vs2013xp/OpenMPT.vcxproj	(revision 7555)
+++ build/vs2013xp/OpenMPT.vcxproj	(working copy)
@@ -1062,6 +1062,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2013xp/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2013xp/OpenMPT.vcxproj.filters	(revision 7555)
+++ build/vs2013xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1169,6 +1169,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015/libopenmpt-small.vcxproj
===================================================================
--- build/vs2015/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2015/libopenmpt-small.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2015/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2015/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015/libopenmpt.vcxproj
===================================================================
--- build/vs2015/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2015/libopenmpt.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2015/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2015/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015/libopenmpt_test.vcxproj
===================================================================
--- build/vs2015/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2015/libopenmpt_test.vcxproj	(working copy)
@@ -366,6 +366,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2015/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2015/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015/OpenMPT.vcxproj
===================================================================
--- build/vs2015/OpenMPT.vcxproj	(revision 7555)
+++ build/vs2015/OpenMPT.vcxproj	(working copy)
@@ -1062,6 +1062,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2015/OpenMPT.vcxproj.filters	(revision 7555)
+++ build/vs2015/OpenMPT.vcxproj.filters	(working copy)
@@ -1178,6 +1178,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015xp/libopenmpt-small.vcxproj
===================================================================
--- build/vs2015xp/libopenmpt-small.vcxproj	(revision 7555)
+++ build/vs2015xp/libopenmpt-small.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015xp/libopenmpt-small.vcxproj.filters
===================================================================
--- build/vs2015xp/libopenmpt-small.vcxproj.filters	(revision 7555)
+++ build/vs2015xp/libopenmpt-small.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015xp/libopenmpt.vcxproj
===================================================================
--- build/vs2015xp/libopenmpt.vcxproj	(revision 7555)
+++ build/vs2015xp/libopenmpt.vcxproj	(working copy)
@@ -538,6 +538,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015xp/libopenmpt.vcxproj.filters
===================================================================
--- build/vs2015xp/libopenmpt.vcxproj.filters	(revision 7555)
+++ build/vs2015xp/libopenmpt.vcxproj.filters	(working copy)
@@ -515,6 +515,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015xp/libopenmpt_test.vcxproj
===================================================================
--- build/vs2015xp/libopenmpt_test.vcxproj	(revision 7555)
+++ build/vs2015xp/libopenmpt_test.vcxproj	(working copy)
@@ -366,6 +366,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015xp/libopenmpt_test.vcxproj.filters
===================================================================
--- build/vs2015xp/libopenmpt_test.vcxproj.filters	(revision 7555)
+++ build/vs2015xp/libopenmpt_test.vcxproj.filters	(working copy)
@@ -533,6 +533,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: build/vs2015xp/OpenMPT.vcxproj
===================================================================
--- build/vs2015xp/OpenMPT.vcxproj	(revision 7555)
+++ build/vs2015xp/OpenMPT.vcxproj	(working copy)
@@ -1062,6 +1062,7 @@
     <ClCompile Include="..\..\soundlib\Load_s3m.cpp" />
     <ClCompile Include="..\..\soundlib\Load_sfx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_stm.cpp" />
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp" />
     <ClCompile Include="..\..\soundlib\Load_ult.cpp" />
     <ClCompile Include="..\..\soundlib\Load_umx.cpp" />
     <ClCompile Include="..\..\soundlib\Load_wav.cpp" />
Index: build/vs2015xp/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2015xp/OpenMPT.vcxproj.filters	(revision 7555)
+++ build/vs2015xp/OpenMPT.vcxproj.filters	(working copy)
@@ -1178,6 +1178,9 @@
     <ClCompile Include="..\..\soundlib\Load_stm.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\soundlib\Load_stp.cpp">
+      <Filter>soundlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\soundlib\Load_ult.cpp">
       <Filter>soundlib</Filter>
     </ClCompile>
Index: installer/filetypes.iss
===================================================================
--- installer/filetypes.iss	(revision 7555)
+++ installer/filetypes.iss	(working copy)
@@ -47,6 +47,7 @@
 Name: "associate_exotic\sfx2"; Description: "SoundFX 2 (SFX2)";
 Name: "associate_exotic\st26"; Description: "SoundTracker 2.6 (ST26)";
 Name: "associate_exotic\stm"; Description: "Scream Tracker 2 (STM)";
+Name: "associate_exotic\stp"; Description: "Soundtracker Pro II (STP)";
 Name: "associate_exotic\ult"; Description: "UltraTracker (ULT)";
 Name: "associate_exotic\umx"; Description: "Unreal Music (UMX)";
 Name: "associate_exotic\wow"; Description: "Grave Composer (WOW)";
@@ -95,6 +96,7 @@
 Root: HKCR; Subkey: ".sfx2"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\sfx2
 Root: HKCR; Subkey: ".st26"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\st26
 Root: HKCR; Subkey: ".stm"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\stm
+Root: HKCR; Subkey: ".stp"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\stp
 Root: HKCR; Subkey: ".ult"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\ult
 Root: HKCR; Subkey: ".umx"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\umx
 Root: HKCR; Subkey: ".wow"; ValueType: string; ValueName: ""; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: associate_exotic\wow
@@ -165,6 +167,7 @@
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".sfx2"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".st26"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".stm"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
+Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".stp"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".ult"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".umx"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Clients\Media\OpenMPT\Capabilities\FileAssociations"; ValueType: string; ValueName: ".wow"; ValueData: "OpenMPTFile"; Flags: uninsdeletevalue; Tasks: not portable
@@ -211,6 +214,7 @@
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".sfx2"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".st26"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".stm"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
+Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".stp"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".ult"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".umx"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
 Root: HKLM; Subkey: "Software\Classes\Applications\mptrack.exe\SupportedTypes"; ValueType: string; ValueName: ".wow"; ValueData: ""; Flags: uninsdeletevalue; Tasks: not portable
Index: libopenmpt/foo_openmpt.cpp
===================================================================
--- libopenmpt/foo_openmpt.cpp	(revision 7555)
+++ libopenmpt/foo_openmpt.cpp	(working copy)
@@ -319,6 +319,7 @@
 	"*.imf" ";"
 	"*.j2b" ";"
 	"*.plm" ";"
+	"*.stp" ";"
 	"*.sfx" ";"
 	"*.sfx2" ";"
 	"*.mms" ";"
Index: mptrack/Mptrack.cpp
===================================================================
--- mptrack/Mptrack.cpp	(revision 7555)
+++ mptrack/Mptrack.cpp	(working copy)
@@ -1431,7 +1431,7 @@
 		"FastTracker Modules (*.xm)|*.xm;*.xmz|"
 		"Impulse Tracker Modules (*.it)|*.it;*.itz|"
 		"OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|"
-		"Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.sfx;*.sfx2;*.mms|"
+		"Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.stp;*.sfx;*.sfx2;*.mms|"
 		"Wave Files (*.wav)|*.wav|"
 		"MIDI Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|"
 		"All Files (*.*)|*.*||")
Index: soundlib/Load_stp.cpp
===================================================================
--- soundlib/Load_stp.cpp	(nonexistent)
+++ soundlib/Load_stp.cpp	(working copy)
@@ -0,0 +1,923 @@
+/*
+ * Load_stp.cpp
+ * ------------
+ * Purpose: STP (Soundtracker Pro II) module loader
+ * Notes  : A few exotic effects aren't supported.
+ *          Multiple sample loops are supported, but only the first 10 can be used as cue points
+ *          (with 16xx and 18xx).
+ *          Fractional speed values and combined auto effects are handled whenever possible,
+ *          but some effects may be omitted (and there may be tempo accuracy issues).
+ * Authors: Devin Acker
+ *          OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ *
+ * Wisdom from the Soundtracker Pro II manual:
+ * "To create shorter patterns, simply create shorter patterns."
+ */
+
+#include "stdafx.h"
+#include "Loaders.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+// File header (except for "STP3" magic)
+struct STPFileHeader
+{
+	uint16be version;
+	uint8be  numOrders;
+	uint8be  patternLength;
+	uint8be  orderList[128];
+	uint16be speed;
+	uint16be speedFrac;
+	uint16be timerCount;
+	uint16be flags;
+	uint32be reserved;
+	uint16be midiCount; // always 50
+	uint8be  midi[50];
+	uint16be numSamples;
+	uint16be sampleStructSize;
+};
+
+MPT_BINARY_STRUCT(STPFileHeader, 200);
+
+
+// Sample header (versions 0 and 1)
+struct STPSampleHeaderOld
+{
+	char     pathName[31];
+	uint8be  flags;
+	char     fileName[30];
+	uint32be length;
+	uint8be  volume;
+	uint8be  reserved1;
+	uint32be loopStart;
+	uint32be loopLength;
+	uint16be defaultCmd;
+	uint32be reserved2;
+
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nVolume = 4u * std::min<uint16>(volume, 64);
+
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		}
+		else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+
+	void Read(FileReader &file)
+	{
+		file.ReadStruct(*this);
+	}
+};
+
+MPT_BINARY_STRUCT(STPSampleHeaderOld, 82);
+
+
+// Sample header (version 2), no endian-defined integers due to variable string length and alignment in file (we read the fields manually)
+struct STPSampleHeader
+{
+	//char pathName[256];
+	uint8  flags;
+	char   fileName[30];
+	uint32 length;
+	uint8  volume;
+	uint8  reserved1;
+	uint32 loopStart;
+	uint32 loopLength;
+	uint16 defaultCmd;
+	uint16 defaultPeriod;
+	uint8  finetune;
+	uint8  reserved2;
+
+	void ConvertToMPT(ModSample &mptSmp) const
+	{
+		mptSmp.Initialize(MOD_TYPE_MOD);
+		mptSmp.nLength = length;
+		mptSmp.nFineTune = static_cast<int8>(finetune << 3);
+		mptSmp.nVolume = 4 * std::min<uint8>(volume, 64);
+
+		mptSmp.nLoopStart = loopStart;
+		mptSmp.nLoopEnd = loopStart + loopLength;
+
+		if(mptSmp.nLoopStart >= mptSmp.nLength)
+		{
+			mptSmp.nLoopStart = mptSmp.nLength - 1;
+		}
+		if(mptSmp.nLoopEnd > mptSmp.nLength)
+		{
+			mptSmp.nLoopEnd = mptSmp.nLength;
+		}
+
+		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
+		{
+			mptSmp.nLoopStart = 0;
+			mptSmp.nLoopEnd = 0;
+		} else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
+		{
+			mptSmp.cues[0] = mptSmp.nLoopStart;
+			mptSmp.uFlags.set(CHN_LOOP);
+		}
+	}
+
+	void Read(FileReader &file)
+	{
+		std::string str;
+
+		file.ReadNullString(str, 255);
+		//mpt::String::Copy(pathName, str);
+
+		flags = file.ReadUint8();
+
+		file.ReadNullString(str, 29);
+		mpt::String::Copy(fileName, str);
+
+		// seek to even boundary
+		if(file.GetPosition() % 2u)
+			file.Skip(1);
+
+		length = file.ReadUint32BE();
+		volume = file.ReadUint8();
+		reserved1 = file.ReadUint8();
+
+		loopStart = file.ReadUint32BE();
+		loopLength = file.ReadUint32BE();
+
+		defaultCmd = file.ReadUint16BE();
+		defaultPeriod = file.ReadUint16BE();
+
+		finetune = file.ReadUint8();
+		reserved2 = file.ReadUint8();
+	}
+};
+
+struct STPLoopInfo
+{
+	SmpLength loopStart;
+	SmpLength loopLength;
+	SAMPLEINDEX looped;
+	SAMPLEINDEX nonLooped;
+};
+
+typedef std::vector<STPLoopInfo> STPLoopList;
+
+template <typename T>
+static void ReadSample(FileReader &file, T &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME])
+//--------------------------------------------------------------------------------------------------------------
+{
+	sampleHeader.Read(file);
+	sampleHeader.ConvertToMPT(sample);
+
+	mpt::String::Read<mpt::String::maybeNullTerminated>(sampleName, sampleHeader.fileName);
+}
+
+
+static TEMPO ConvertTempo(uint16 ciaSpeed)
+//----------------------------------------
+{
+	// 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
+	return TEMPO((125.0 * 3546.0) / ciaSpeed);
+}
+
+
+static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
+//------------------------------------------------------------------------------------------------------
+{
+	if(!src.HasSampleData()) return;
+
+	dest.FreeSample();
+
+	dest = src;
+	dest.nLength = len;
+	dest.pSample = nullptr;
+
+	if(!dest.AllocateSample())
+	{
+		return;
+	}
+
+	// only preserve cue points if the target sample length is the same
+	if(len != src.nLength)
+		MemsetZero(dest.cues);
+
+	std::memcpy(dest.pSample8, src.pSample8 + start, len);
+	if(loop)
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = len;
+		dest.uFlags.set(CHN_LOOP);
+	} else
+	{
+		dest.nLoopStart = 0;
+		dest.nLoopEnd = 0;
+		dest.uFlags.reset(CHN_LOOP);
+	}
+}
+
+static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
+//--------------------------------------------------------------------
+{
+	// This should only modify a sample if it has more than one loop
+	// (otherwise, it behaves like a normal sample loop)
+	if(!smp.HasSampleData() || loopList.size() < 2) return;
+
+	ModSample newSmp = smp;
+	newSmp.nLength = 0;
+	newSmp.pSample = nullptr;
+
+	size_t numLoops = loopList.size();
+
+	// get the total length of the sample after combining all looped sections
+	for(size_t i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+
+		// if adding this loop would cause the sample length to exceed maximum,
+		// then limit and bail out
+		if((newSmp.nLength + info.loopLength > MAX_SAMPLE_LENGTH) ||
+		   (info.loopLength > MAX_SAMPLE_LENGTH) ||
+		   (info.loopStart + info.loopLength > smp.nLength))
+		{
+			numLoops = i;
+			break;
+		}
+
+		newSmp.nLength += info.loopLength;
+	}
+
+	if(!newSmp.AllocateSample())
+	{
+		return;
+	}
+
+	// start copying the looped sample data parts
+	SmpLength start = 0;
+
+	for(size_t i = 0; i < numLoops; i++)
+	{
+		STPLoopInfo &info = loopList[i];
+
+		memcpy(newSmp.pSample8 + start, smp.pSample8 + info.loopStart, info.loopLength);
+
+		// update loop info based on position in edited sample
+		info.loopStart = start;
+		if(i > 0 && i <= CountOf(newSmp.cues))
+		{
+			newSmp.cues[i - 1] = start;
+		}
+		start += info.loopLength;
+	}
+
+	// replace old sample with new one
+	smp.FreeSample();
+	smp = newSmp;
+
+	smp.nLoopStart = 0;
+	smp.nLoopEnd = smp.nLength;
+	smp.uFlags.set(CHN_LOOP);
+}
+
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+	file.Rewind();
+	if(!file.ReadMagic("STP3"))
+		return false;
+
+	STPFileHeader fileHeader;
+	file.ReadStruct(fileHeader);
+	if(fileHeader.version > 2
+		|| fileHeader.numOrders > 128
+		|| fileHeader.numSamples >= MAX_SAMPLES
+		|| fileHeader.midiCount != 50)
+		return false;
+
+	if(loadFlags == onlyVerifyHeader)
+		return true;
+
+	InitializeGlobals(MOD_TYPE_STP);
+
+	m_nChannels = 4;
+	m_nSamples = 0;
+	m_nInstruments = 0;
+
+	m_nDefaultSpeed = fileHeader.speed;
+	m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
+
+	m_nMinPeriod = 14 * 4;
+	m_nMaxPeriod = 3424 * 4;
+
+	Order.ReadFromArray(fileHeader.orderList, fileHeader.numOrders);
+
+	std::vector<STPLoopList> loopInfo;
+	// non-looped versions of samples with loops (when needed)
+	std::vector<SAMPLEINDEX> nonLooped;
+
+	// Load sample headers
+	SAMPLEINDEX samplesInFile = 0;
+
+	for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++)
+	{
+		// this is 1-based the same as smp
+		SAMPLEINDEX actualSmp = file.ReadUint16BE();
+		if(actualSmp >= MAX_SAMPLES)
+			return false;
+
+		samplesInFile = m_nSamples = std::max(m_nSamples, actualSmp);
+
+		ModSample &thisSmp = Samples[actualSmp];
+
+		if(fileHeader.version == 2)
+		{
+			STPSampleHeader sampleHeader;
+			uint32 headerSize = file.ReadUint32BE();
+
+			ReadSample(file, sampleHeader, thisSmp, m_szNames[actualSmp]);
+			// TODO: verify string lengths against headerSize?
+			MPT_UNUSED_VARIABLE(headerSize);
+		} else
+		{
+			STPSampleHeaderOld sampleHeaderOld;
+			ReadSample(file, sampleHeaderOld, thisSmp, m_szNames[actualSmp]);
+		}
+
+		STPLoopList loopList;
+
+		if(fileHeader.version >= 1)
+		{
+			uint16 numLoops = file.ReadUint16BE();
+
+			STPLoopInfo loop;
+			loop.looped = loop.nonLooped = 0;
+
+			if(numLoops == 0 && thisSmp.uFlags[CHN_LOOP])
+			{
+				loop.loopStart  = thisSmp.nLoopStart;
+				loop.loopLength = thisSmp.nLoopEnd - thisSmp.nLoopStart;
+				loopList.push_back(loop);
+			} else for(SAMPLEINDEX i = 0; i < numLoops; i++)
+			{
+				loop.loopStart  = file.ReadUint32BE();
+				loop.loopLength = file.ReadUint32BE();
+				loopList.push_back(loop);
+			}
+		}
+
+		if(actualSmp)
+		{
+			nonLooped.resize(actualSmp);
+			loopInfo.resize(actualSmp);
+			loopInfo[actualSmp - 1] = loopList;
+		}
+	}
+
+	// Load patterns
+	uint16 numPatterns = 128;
+	if(fileHeader.version == 0)
+		numPatterns = file.ReadUint16BE();
+
+	uint16 patternLength = fileHeader.patternLength;
+	CHANNELINDEX channels = 4;
+	if(fileHeader.version > 0)
+	{
+		// Scan for total number of channels
+		FileReader::off_t patOffset = file.GetPosition();
+		for(uint16 pat = 0; pat < numPatterns; pat++)
+		{
+			PATTERNINDEX actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+			m_nChannels = std::max(m_nChannels, channels);
+
+			file.Skip(channels * patternLength * 4u);
+		}
+		file.Seek(patOffset);
+		if(m_nChannels > MAX_BASECHANNELS)
+			return false;
+	}
+
+	uint8 globalVolSlide = 0;
+	std::vector<uint8> autoFinePorta(m_nChannels, 0);
+	std::vector<uint8> autoPortaUp(m_nChannels, 0);
+	std::vector<uint8> autoPortaDown(m_nChannels, 0);
+	std::vector<uint8> autoVolSlide(m_nChannels, 0);
+	std::vector<uint8> autoVibrato(m_nChannels, 0);
+	std::vector<uint8> vibratoMem(m_nChannels, 0);
+	std::vector<uint8> autoTremolo(m_nChannels, 0);
+	std::vector<uint8> autoTonePorta(m_nChannels, 0);
+	std::vector<uint8> tonePortaMem(m_nChannels, 0);
+
+	for(uint16 pat = 0; pat < numPatterns; pat++)
+	{
+		PATTERNINDEX actualPat = pat;
+
+		if(fileHeader.version > 0)
+		{
+			actualPat = file.ReadUint16BE();
+			if(actualPat == 0xFFFF)
+				break;
+
+			patternLength = file.ReadUint16BE();
+			channels = file.ReadUint16BE();
+		}
+
+		if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
+		{
+			file.Skip(channels * patternLength * 4u);
+			continue;
+		}
+
+		for(ROWINDEX row = 0; row < patternLength; row++)
+		{
+			PatternRow rowBase = Patterns[actualPat].GetpModCommand(row, 0);
+
+			bool didGlobalVolSlide = false;
+			bool shouldDelay = false;
+
+			// if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
+			switch(fileHeader.speedFrac & 3)
+			{
+			// 1/4
+			case 1: shouldDelay = (row & 3) == 0; break;
+			// 1/2
+			case 2: shouldDelay = (row & 1) == 0; break;
+			// 3/4
+			case 3: shouldDelay = (row & 3) != 3; break;
+			}
+
+			for(CHANNELINDEX chn = 0; chn < channels; chn++)
+			{
+				ModCommand &m = rowBase[chn];
+				uint8 data[4];
+				file.ReadArray(data);
+
+				m.instr   = data[0];
+				m.note    = data[1];
+				m.command = data[2];
+				m.param   = data[3];
+
+				if(m.note)
+				{
+					m.note += 24 + NOTE_MIN;
+
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoVolSlide[chn] = 0;
+					autoVibrato[chn] = vibratoMem[chn] = 0;
+					autoTremolo[chn] = 0;
+					autoTonePorta[chn] = tonePortaMem[chn] = 0;
+				}
+
+				// this is a nibble-swapped param value used for auto fine volside
+				// and auto global fine volside
+				uint8 swap = (m.param >> 4) | (m.param << 4);
+
+				if((m.command & 0xF0) == 0xF0)
+				{
+					m.param = mpt::saturate_cast<ModCommand::PARAM>(Util::Round(ConvertTempo(m.param | (((uint16)m.command & 0xF) << 8)).ToDouble()));
+					m.command = CMD_TEMPO;
+				} else switch(m.command)
+				{
+				case 0x00: // arpeggio
+					if(m.param)
+						m.command = CMD_ARPEGGIO;
+					break;
+
+				case 0x01: // portamento up
+					m.command = CMD_PORTAMENTOUP;
+					break;
+
+				case 0x02: // portamento down
+					m.command = CMD_PORTAMENTODOWN;
+					break;
+
+				case 0x03: // auto fine portamento up
+					autoFinePorta[chn] = 0x10 | std::min(m.param, ModCommand::PARAM(15));
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x04: // auto fine portamento down
+					autoFinePorta[chn] = 0x20 | std::min(m.param, ModCommand::PARAM(15));
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x05: // auto portamento up
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = m.param;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x06: // auto portamento down
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = m.param;
+					autoTonePorta[chn] = 0;
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x07: // set global volume
+					m.command = CMD_GLOBALVOLUME;
+					globalVolSlide = 0;
+					break;
+
+				case 0x08: // auto global fine volume slide
+					globalVolSlide = swap;
+					m.command = m.param = 0;
+					break;
+
+				case 0x09: // fine portamento up
+					m.command = CMD_MODCMDEX;
+					m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x0A: // fine portamento down
+					m.command = CMD_MODCMDEX;
+					m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x0B: // auto fine volume slide
+					autoVolSlide[chn] = swap;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0C: // set volume
+					m.volcmd = VOLCMD_VOLUME;
+					m.vol = m.param;
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0D: // volume slide (param is swapped compared to .mod)
+					if(m.param & 0xF0)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEDOWN;
+						m.vol = m.param >> 4;
+					} else if(m.param & 0x0F)
+					{
+						m.volcmd = VOLCMD_VOLSLIDEUP;
+						m.vol = m.param & 0xF;
+					}
+					autoVolSlide[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x0E: // set filter (also uses opposite value compared to .mod)
+					m.command = CMD_MODCMDEX;
+					m.param = 1 ^ (m.param ? 1 : 0);
+					break;
+
+				case 0x0F: // set speed
+					m.command = CMD_SPEED;
+					fileHeader.speedFrac = m.param & 0xF;
+					m.param >>= 4;
+					break;
+
+				case 0x10: // auto vibrato
+					autoVibrato[chn] = m.param;
+					vibratoMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x11: // auto tremolo
+					if(m.param & 0xF)
+						autoTremolo[chn] = m.param;
+					else
+						autoTremolo[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x12: // pattern break
+					m.command = CMD_PATTERNBREAK;
+					break;
+
+				case 0x13: // auto tone portamento
+					autoFinePorta[chn] = 0;
+					autoPortaUp[chn] = 0;
+					autoPortaDown[chn] = 0;
+					autoTonePorta[chn] = m.param;
+
+					tonePortaMem[chn] = 0;
+					m.command = m.param = 0;
+					break;
+
+				case 0x14: // position jump
+					m.command = CMD_POSITIONJUMP;
+					break;
+
+				case 0x16: // start loop sequence
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList &loopList = loopInfo[m.instr - 1];
+
+						m.param--;
+						if(m.param < std::min(MPT_ARRAY_COUNT(ModSample().cues), loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x17: // play only loop nn
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList &loopList = loopInfo[m.instr - 1];
+
+						m.param--;
+						if(m.param < loopList.size())
+						{
+							if(!loopList[m.param].looped && m_nSamples < MAX_SAMPLES - 1)
+								loopList[m.param].looped = ++m_nSamples;
+							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x18: // play sequence without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList &loopList = loopInfo[m.instr - 1];
+
+						m.param--;
+						if(m.param < std::min(MPT_ARRAY_COUNT(ModSample().cues), loopList.size()))
+						{
+							m.volcmd = VOLCMD_OFFSET;
+							m.vol = m.param;
+						}
+						// switch to non-looped version of sample and create it if needed
+						if(!nonLooped[m.instr - 1] && m_nSamples < MAX_SAMPLES - 1)
+							nonLooped[m.instr - 1] = ++m_nSamples;
+						m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x19: // play only loop nn without loop
+					if(m.instr && m.instr <= loopInfo.size())
+					{
+						STPLoopList &loopList = loopInfo[m.instr - 1];
+
+						m.param--;
+						if(m.param < loopList.size())
+						{
+							if(!loopList[m.param].nonLooped && m_nSamples < MAX_SAMPLES-1)
+								loopList[m.param].nonLooped = ++m_nSamples;
+							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
+						}
+					}
+
+					m.command = m.param = 0;
+					break;
+
+				case 0x1D: // fine volume slide (nibble order also swapped)
+					m.command = CMD_VOLUMESLIDE;
+					m.param = swap;
+					if(m.param & 0xF0) // slide down
+						m.param |= 0x0F;
+					else if(m.param & 0x0F)
+						m.param |= 0xF0;
+					break;
+
+				case 0x20: // "delayed fade"
+					// just behave like either a normal fade or a notecut
+					// depending on the speed
+					if(m.param & 0xF0)
+					{
+						autoVolSlide[chn] = m.param >> 4;
+						m.command = m.param = 0;
+					} else
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = 0xC0 | (m.param & 0xF);
+					}
+					break;
+
+				case 0x21: // note delay
+					m.command = CMD_MODCMDEX;
+					m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x22: // retrigger note
+					m.command = CMD_RETRIG;
+					m.param = std::min(m.param, ModCommand::PARAM(15));
+					break;
+
+				case 0x49: // set sample offset
+					m.command = CMD_OFFSET;
+					break;
+
+				case 0x4E: // other protracker commands (pattern loop / delay)
+					if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
+						m.command = CMD_MODCMDEX;
+					else
+						m.command = m.param = 0;
+					break;
+
+				case 0x4F: // set speed/tempo
+					if(m.param < 0x20)
+					{
+						m.command = CMD_SPEED;
+						fileHeader.speedFrac = 0;
+					} else
+					{
+						m.command = CMD_TEMPO;
+					}
+					break;
+
+				default:
+					m.command = CMD_NONE;
+					break;
+				}
+
+				bool didVolSlide = false;
+
+				// try to put volume slide in volume command
+				if(autoVolSlide[chn] && !m.volcmd)
+				{
+					if(autoVolSlide[chn] & 0xF0)
+					{
+						m.volcmd = VOLCMD_FINEVOLUP;
+						m.vol = autoVolSlide[chn] >> 4;
+					} else
+					{
+						m.volcmd = VOLCMD_FINEVOLDOWN;
+						m.vol = autoVolSlide[chn] & 0xF;
+					}
+					didVolSlide = true;
+				}
+
+				// try to place/combine all remaining running effects.
+				if(m.command == CMD_NONE)
+				{
+					if(autoPortaUp[chn])
+					{
+						m.command = CMD_PORTAMENTOUP;
+						m.param = autoPortaUp[chn];
+
+					} else if(autoPortaDown[chn])
+					{
+						m.command = CMD_PORTAMENTODOWN;
+						m.param = autoPortaDown[chn];
+
+					} else if(autoFinePorta[chn])
+					{
+						m.command = CMD_MODCMDEX;
+						m.param = autoFinePorta[chn];
+
+					} else if(autoTonePorta[chn])
+					{
+						m.command = CMD_TONEPORTAMENTO;
+						m.param = tonePortaMem[chn] = autoTonePorta[chn];
+
+					} else if(autoVibrato[chn])
+					{
+						m.command = CMD_VIBRATO;
+						m.param = vibratoMem[chn] = autoVibrato[chn];
+
+					} else if(!didVolSlide && autoVolSlide[chn])
+					{
+						m.command = CMD_VOLUMESLIDE;
+						m.param = autoVolSlide[chn];
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+						didVolSlide = true;
+
+					} else if(autoTremolo[chn])
+					{
+						m.command = CMD_TREMOLO;
+						m.param = autoTremolo[chn];
+
+					} else if(shouldDelay)
+					{
+						// insert a fine pattern delay here
+						m.command = CMD_S3MCMDEX;
+						m.param = 0x61;
+						shouldDelay = false;
+
+					} else if(!didGlobalVolSlide && globalVolSlide)
+					{
+						m.command = CMD_GLOBALVOLSLIDE;
+						m.param = globalVolSlide;
+						// convert to a "fine" value by setting the other nibble to 0xF
+						if(m.param & 0x0F)
+							m.param |= 0xF0;
+						else if(m.param & 0xF0)
+							m.param |= 0x0F;
+
+						didGlobalVolSlide = true;
+					}
+				}
+			}
+
+			// TODO: create/use extra channels for global volslide/delay if needed
+		}
+	}
+
+	// after we know how many channels there really are...
+	m_nSamplePreAmp = 256 / m_nChannels;
+	// Setup channel pan positions and volume
+	SetupMODPanning(true);
+
+	// Skip over scripts and drumpad info
+	if(fileHeader.version > 0)
+	{
+		while(file.CanRead(2))
+		{
+			uint16 scriptNum = file.ReadUint16BE();
+			if(scriptNum == 0xFFFF)
+				break;
+
+			file.Skip(2);
+			uint32 length = file.ReadUint32BE();
+			file.Skip(length);
+		}
+
+		// Skip drumpad stuff
+		file.Skip(17*2);
+	}
+
+	// Reading samples
+	if(loadFlags & loadSampleData)
+	{
+		for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
+		{
+			SampleIO(
+				SampleIO::_8bit,
+				SampleIO::mono,
+				SampleIO::littleEndian,
+				SampleIO::signedPCM)
+				.ReadSample(Samples[smp], file);
+
+			ConvertLoopSequence(Samples[smp], loopInfo[smp - 1]);
+
+			// make a non-looping duplicate of this sample if needed
+			if(nonLooped[smp - 1])
+			{
+				ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp-1]], 0, Samples[smp].nLength, false);
+			}
+
+			for(SAMPLEINDEX loop = 0; loop < loopInfo[smp - 1].size(); loop++)
+			{
+				STPLoopInfo &info = loopInfo[smp - 1][loop];
+
+				// make duplicate samples for this individual section if needed
+				if(info.looped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
+				}
+				if(info.nonLooped)
+				{
+					ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+OPENMPT_NAMESPACE_END

Property changes on: soundlib/Load_stp.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/x-c++src
\ No newline at end of property
Index: soundlib/Snd_defs.h
===================================================================
--- soundlib/Snd_defs.h	(revision 7555)
+++ soundlib/Snd_defs.h	(working copy)
@@ -98,6 +98,7 @@
 	MOD_TYPE_UAX	= 0x10000000, // sampleset as module
 	MOD_TYPE_PLM	= 0x20000000,
 	MOD_TYPE_SFX	= 0x40000000,
+	MOD_TYPE_STP	= 0x80000000,
 };
 DECLARE_FLAGSET(MODTYPE)
 
Index: soundlib/Snd_fx.cpp
===================================================================
--- soundlib/Snd_fx.cpp	(revision 7555)
+++ soundlib/Snd_fx.cpp	(working copy)
@@ -3434,7 +3434,7 @@
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, param, doFineSlides);
@@ -3493,7 +3493,7 @@
 	else
 		param = pChn->nOldPortaUpDown;
 
-	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
 
 	// Process MIDI pitch bend for instrument plugins
 	MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
@@ -3989,7 +3989,7 @@
 	else
 		param = pChn->nOldVolumeSlide;
 
-	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
+	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP)))
 	{
 		// MOD / XM nibble priority
 		if((param & 0xF0) != 0)
Index: soundlib/Sndfile.cpp
===================================================================
--- soundlib/Sndfile.cpp	(revision 7555)
+++ soundlib/Sndfile.cpp	(working copy)
@@ -309,6 +309,7 @@
 			 && !ReadJ2B(file, loadFlags)
 			 && !ReadPT36(file, loadFlags)
 			 && !ReadSFX(file, loadFlags)
+			 && !ReadSTP(file, loadFlags)
 			 && !ReadMod(file, loadFlags)
 			 && !ReadICE(file, loadFlags)
 			 && !Read669(file, loadFlags)
@@ -1075,6 +1076,7 @@
 	case MOD_TYPE_AMF0:
 	case MOD_TYPE_DIGI:
 	case MOD_TYPE_SFX:
+	case MOD_TYPE_STP:
 		return MOD_TYPE_MOD;
 	case MOD_TYPE_MED:
 		if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments)
Index: soundlib/Sndfile.h
===================================================================
--- soundlib/Sndfile.h	(revision 7555)
+++ soundlib/Sndfile.h	(working copy)
@@ -680,6 +680,7 @@
 	bool ReadS3M(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadSFX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+	bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadUMX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 	bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
@@ -884,7 +885,7 @@
 	// Returns true if the current format uses transpose+finetune rather than frequency in Hz to specify middle-C.
 	static bool UseFinetuneAndTranspose(MODTYPE type)
 	{
-		return (type & (MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_MTM | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_XM));
+		return (type & (MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_MTM | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP | MOD_TYPE_XM));
 	}
 	bool UseFinetuneAndTranspose() const
 	{
Index: soundlib/Tables.cpp
===================================================================
--- soundlib/Tables.cpp	(revision 7555)
+++ soundlib/Tables.cpp	(working copy)
@@ -90,6 +90,7 @@
 	{ MOD_TYPE_SFX,		"SoundFX",					"sfx" },
 	{ MOD_TYPE_SFX,		"SoundFX",					"sfx2" },
 	{ MOD_TYPE_SFX,		"MultiMedia Sound",			"mms" },
+	{ MOD_TYPE_STP,		"Soundtracker Pro II",		"stp" },
 
 #ifndef NO_ARCHIVE_SUPPORT
 	// Compressed modules
@@ -151,6 +152,7 @@
 	{ MOD_TYPE_DBM , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_DIGI, mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_SFX , mpt::CharsetISO8859_1  },
+	{ MOD_TYPE_STP,  mpt::CharsetISO8859_1  },
 	// Amiga // DOS
 	{ MOD_TYPE_MOD , mpt::CharsetISO8859_1  },
 	{ MOD_TYPE_MED , mpt::CharsetISO8859_1  },
stpro2_r5.patch (70,513 bytes)   
Revenant

Revenant

2017-02-07 23:43

reporter   ~0002867

Hi, sorry I haven't replied to this issue recently; I've been a bit busy with work and other projects, etc.

I haven't checked out any of the mods Johan posted yet, but I'll take a look at both those and the latest version of the patch sometime soon.

I wish I had had more time to spend revising the code / finding more test cases back when I first submitted this, but thanks for keeping it under consideration since then.

Saga Musix

Saga Musix

2017-02-08 19:15

administrator   ~0002868

Last edited: 2017-02-08 19:27

Seems like it has something to do with ignoring the sample header size in v2 files. Not ignoring that makes the file load, but the sample sizes are way off.
This is what I did:

STPSampleHeader sampleHeader;
uint32 headerSize = file.ReadUint32BE();
FileReader chunk = file.ReadChunk(headerSize - 2);
ReadSample(chunk, sampleHeader, thisSmp, m_szNames[actualSmp]);

Edit: Got it to work. The string size limits were off by one. Now the file loads!

Saga Musix

Saga Musix

2017-02-08 20:31

administrator   ~0002869

Merged as of r7568. Thanks a lot for your contribution once again. If you have any further improvements to the loader, do not hesitate to submit them!

Now I just have to restart all the fuzzers to find all the new crashes we just introduced. ;)

Saga Musix

Saga Musix

2017-02-10 18:32

administrator   ~0002873

Hm, I noticed that ModSample::cues[0] is only set for v2 files, but v1 files can also contains loop slices and cue points as far as I can tell. Is this intentional? My guess would be that cues[0] should be set for both v1 and v2 files.

Revenant

Revenant

2017-02-11 00:38

reporter   ~0002874

Yeah, I believe it should be done both for v1 and v2 files. It looks like in my original versions of the patch I was just forgetting to set it in STPSampleHeaderOld::ConvertToMPT.

Saga Musix

Saga Musix

2017-02-11 00:43

administrator   ~0002875

Okay, fixed in r7583.

Issue History

Date Modified Username Field Change
2016-03-05 04:15 Revenant New Issue
2016-03-05 04:15 Revenant File Added: stpro2.patch
2016-03-05 09:06 manx Note Added: 0002274
2016-03-05 09:37 Revenant Note Added: 0002275
2016-03-05 10:30 Saga Musix Note Added: 0002276
2016-03-10 05:41 manx Assigned To => Saga Musix
2016-03-10 05:41 manx Status new => assigned
2016-03-13 16:32 Saga Musix Note Added: 0002283
2016-03-13 16:32 Saga Musix Note Edited: 0002283
2016-03-13 18:44 Revenant Note Added: 0002284
2016-03-14 06:08 Revenant Note Added: 0002285
2016-03-14 12:25 Saga Musix Note Added: 0002286
2016-03-14 16:53 Revenant Note Added: 0002287
2016-03-14 16:55 Saga Musix Note Added: 0002288
2016-03-31 06:31 Revenant File Added: stpro2_r2.patch
2016-03-31 06:36 Revenant Note Added: 0002314
2016-04-24 00:29 Saga Musix Target Version => OpenMPT 1.26.01.00 / libopenmpt 0.2-beta17 (upgrade first)
2016-04-30 13:18 Saga Musix Note Added: 0002356
2016-04-30 13:19 Saga Musix Note Edited: 0002356
2016-04-30 13:20 Saga Musix Note Edited: 0002356
2016-04-30 13:25 Saga Musix Note Edited: 0002356
2016-04-30 13:34 Saga Musix Note Added: 0002357
2016-05-17 22:38 Saga Musix Target Version OpenMPT 1.26.01.00 / libopenmpt 0.2-beta17 (upgrade first) => OpenMPT 1.?? (long term goals)
2016-06-08 23:49 Saga Musix File Added: stpro2_r3.patch
2016-06-08 23:51 Saga Musix Note Added: 0002446
2016-08-20 12:34 Saga Musix File Added: stpro2_r4.patch
2016-08-20 12:35 Saga Musix Note Added: 0002621
2016-08-22 17:58 johan.bakker Note Added: 0002624
2017-02-07 17:51 Saga Musix Note Added: 0002865
2017-02-07 17:52 Saga Musix Target Version OpenMPT 1.?? (long term goals) => OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first)
2017-02-07 18:37 Saga Musix File Added: stpro2_r5.patch
2017-02-07 18:37 Saga Musix Note Added: 0002866
2017-02-07 23:43 Revenant Note Added: 0002867
2017-02-08 19:15 Saga Musix Note Added: 0002868
2017-02-08 19:25 Saga Musix Note Edited: 0002868
2017-02-08 19:27 Saga Musix Note Edited: 0002868
2017-02-08 20:30 Saga Musix Note View State: 0002866: private
2017-02-08 20:31 Saga Musix Note View State: 0002866: public
2017-02-08 20:31 Saga Musix Status assigned => resolved
2017-02-08 20:31 Saga Musix Resolution open => fixed
2017-02-08 20:31 Saga Musix Fixed in Version => OpenMPT 1.27.01.00 / libopenmpt 0.3.1 (upgrade first)
2017-02-08 20:31 Saga Musix Note Added: 0002869
2017-02-10 18:32 Saga Musix Note Added: 0002873
2017-02-11 00:38 Revenant Status resolved => feedback
2017-02-11 00:38 Revenant Resolution fixed => reopened
2017-02-11 00:38 Revenant Note Added: 0002874
2017-02-11 00:43 Saga Musix Status feedback => resolved
2017-02-11 00:43 Saga Musix Resolution reopened => fixed
2017-02-11 00:43 Saga Musix Note Added: 0002875