Beispiel #1
0
	void operator() (ModCommand &m)
	{
		const CHANNELINDEX curChn = chn;
		chn++;
		if(chn >= sndFile.GetNumChannels())
		{
			chn = 0;
		}

		if(m.IsPcNote())
		{
			return;
		}

		if(sndFile.GetType() == MOD_TYPE_S3M)
		{
			// Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831).
			// So for tracks made with older versions of OpenMPT, we limit invalid global volume commands.
			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00) && m.command == CMD_GLOBALVOLUME)
			{
				LimitMax(m.param, ModCommand::PARAM(64));
			}
		}

		else if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
		{
			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) ||
				(!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
			{
				if(m.command == CMD_GLOBALVOLUME)
				{
					// Out-of-range global volume commands should be ignored in IT.
					// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
					// So for tracks made with older versions than OpenMPT 1.17.03.02 or tracks made with 1.17.03.02 <= version < 1.20, we limit invalid global volume commands.
					LimitMax(m.param, ModCommand::PARAM(128));
				}

				// SC0 and SD0 should be interpreted as SC1 and SD1 in IT files.
				// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
				else if(m.command == CMD_S3MCMDEX)
				{
					if(m.param == 0xC0)
					{
						m.command = CMD_NONE;
						m.note = NOTE_NOTECUT;
					} else if(m.param == 0xD0)
					{
						m.command = CMD_NONE;
					}
				}
			}

			// In the IT format, slide commands with both nibbles set should be ignored.
			// For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well.
			const bool noteVolSlide =
				(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00) ||
				(!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
				&&
				(m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE);

			// OpenMPT 1.20 also fixes this for global volume and channel volume slides.
			const bool chanVolSlide =
				(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
				&&
				(m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE);

			if(noteVolSlide || chanVolSlide)
			{
				if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0)
				{
					m.param &= 0x0F;
				}
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 01, 04)
				&& sndFile.m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00))	// Ignore compatibility export
			{
				// OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample.
				if(sndFile.GetNumInstruments() && m.instr > sndFile.GetNumInstruments() && !compatPlay)
				{
					m.volcmd = VOLCMD_VOLUME;
					m.vol = 0;
				}
			}
		}

		else if(sndFile.GetType() == MOD_TYPE_XM)
		{
			// Something made be believe that out-of-range global volume commands are ignored in XM
			// just like they are ignored in IT, but apparently they are not. Aaaaaargh!
			if(((sndFile.m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02) && compatPlay) || (sndFile.m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
				&& sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 02, 02)
				&& m.command == CMD_GLOBALVOLUME
				&& m.param > 64)
			{
				m.command = CMD_NONE;
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00)
				|| (!compatPlay && sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
			{
				if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_TONEPORTAMENTO)
				{
					// If there are both a portamento and an offset effect, the portamento should be preferred in XM files.
					// OpenMPT 1.19 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
					m.command = CMD_NONE;
				}
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 01, 10)
				&& m.volcmd == VOLCMD_TONEPORTAMENTO && m.command == CMD_TONEPORTAMENTO
				&& (m.vol != 0 || compatPlay) && m.param != 0)
			{
				// Mx and 3xx on the same row does weird things in FT2: 3xx is completely ignored and the Mx parameter is doubled. Fixed in revision 1312 / OpenMPT 1.20.01.10
				// Previously the values were just added up, so let's fix this!
				m.volcmd = VOLCMD_NONE;
				const uint16 param = static_cast<uint16>(m.param) + static_cast<uint16>(m.vol << 4);
				m.param = mpt::saturate_cast<ModCommand::PARAM>(param);
			}

			if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 09)
				&& m.command == CMD_SPEED && m.param == 0)
			{
				// OpenMPT can emulate FT2's F00 behaviour now.
				m.command = CMD_NONE;
			}
		}

		if(sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
		{
			// Pattern Delay fixes

			const bool fixS6x = (m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0x60);
			// We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files.
			// We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway.
			const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60
				&& (!(compatPlay && sndFile.GetType() == MOD_TYPE_XM) || sndFile.m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)));

			if(fixS6x || fixX6x)
			{
				// OpenMPT 1.20 fixes multiple fine pattern delays on the same row. Previously, only the last command was considered,
				// but all commands should be added up. Since Scream Tracker 3 itself doesn't support S6x, we also use Impulse Tracker's behaviour here,
				// since we can assume that most S3Ms that make use of S6x were composed with Impulse Tracker.
				for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++)
				{
					if((fixCmd->command == CMD_S3MCMDEX || fixCmd->command == CMD_XFINEPORTAUPDOWN) && (fixCmd->param & 0xF0) == 0x60)
					{
						fixCmd->command = CMD_NONE;
					}
				}
			}

			if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xE0)
			{
				// OpenMPT 1.20 fixes multiple pattern delays on the same row. Previously, only the *last* command was considered,
				// but Scream Tracker 3 and Impulse Tracker only consider the *first* command.
				for(ModCommand *fixCmd = (&m) - curChn; fixCmd < &m; fixCmd++)
				{
					if(fixCmd->command == CMD_S3MCMDEX && (fixCmd->param & 0xF0) == 0xE0)
					{
						fixCmd->command = CMD_NONE;
					}
				}
			}
		}

		// Volume column offset in IT/XM is bad, mkay?
		if(sndFile.GetType() != MOD_TYPE_MPT && m.volcmd == VOLCMD_OFFSET && m.command == CMD_NONE)
		{
			m.command = CMD_OFFSET;
			m.param = m.vol << 3;
			m.volcmd = VOLCMD_NONE;
		}

	}
Beispiel #2
0
void CSoundFile::UpgradeModule()
//------------------------------
{
	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 46) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 17, 00, 00))
	{
		// Compatible playback mode didn't exist in earlier versions, so definitely disable it.
		m_playBehaviour.reset(MSF_COMPATIBLE_PLAY);
	}

	const bool compatModeIT = m_playBehaviour[MSF_COMPATIBLE_PLAY] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT));
	const bool compatModeXM = m_playBehaviour[MSF_COMPATIBLE_PLAY] && GetType() == MOD_TYPE_XM;

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
	{
		for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr)
		{
			ModInstrument *ins = Instruments[i];
			// Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead.
			ins->nVolSwing = static_cast<uint8>(std::min<uint32>(ins->nVolSwing * 100 / 64, 100));

			if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00))
			{
				// Previously, Pitch/Pan Separation was only half depth.
				// This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well.
				ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2;
			}

			if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02))
			{
				// IT compatibility 24. Short envelope loops
				// Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM).
				// This was corrected in compatible mode in OpenMPT 1.17.03.02, and in OpenMPT 1.20 it is corrected in normal mode as well.
				ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType());
			}

			if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50))
			{
				// If there are any plugins that can receive volume commands, enable volume bug emulation.
				if(ins->nMixPlug && ins->HasValidMIDIChannel())
				{
					m_playBehaviour.set(kMIDICCBugEmulation);
				}
			}

			if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50) && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing))
			{
				// If there are any instruments with random variation, enable the old random variation behaviour.
				m_playBehaviour.set(kMPTOldSwingBehaviour);
				break;
			}
		}

		if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || !compatModeIT))
		{
			// In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep".
			// In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well,
			// so we have to fix the setting while loading.
			for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++)
			{
				if(Samples[i].nVibSweep == 0 && (Samples[i].nVibDepth | Samples[i].nVibRate))
				{
					Samples[i].nVibSweep = 255;
				}
			}
		}

		// Fix old nasty broken (non-standard) MIDI configs in files.
		m_MidiCfg.UpgradeMacros();
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 02, 10)
		&& m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00)
		&& (GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT)))
	{
		bool instrPlugs = false;
		// Old pitch wheel commands were closest to sample pitch bend commands if the PWD is 13.
		for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
		{
			if(Instruments[i] != nullptr && Instruments[i]->nMidiChannel != MidiNoChannel)
			{
				Instruments[i]->midiPWD = 13;
				instrPlugs = true;
			}
		}
		if(instrPlugs)
		{
			m_playBehaviour.set(kOldMIDIPitchBends);
		}
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 03, 12)
		&& m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00)
		&& (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
		&& (m_playBehaviour[MSF_COMPATIBLE_PLAY] || m_playBehaviour[kMPTOldSwingBehaviour]))
	{
		// The "correct" pan swing implementation did nothing if the instrument also had a pan envelope.
		// If there's a pan envelope, disable pan swing for such modules.
		for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
		{
			if(Instruments[i] != nullptr && Instruments[i]->nPanSwing != 0 && Instruments[i]->PanEnv.dwFlags[ENV_ENABLED])
			{
				Instruments[i]->nPanSwing = 0;
			}
		}
	}

#ifndef NO_PLUGINS
	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 01))
	{
		// Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name)
		for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
		{
#if defined(MODPLUG_TRACKER)
			const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, m_MixPlugins[i].Info.szLibraryName);
#else
			const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, m_MixPlugins[i].Info.szLibraryName);
#endif
			mpt::String::Copy(m_MixPlugins[i].Info.szLibraryName, name);
		}
	}
#endif // NO_PLUGINS

	// Starting from OpenMPT 1.22.07.19, FT2-style panning was applied in compatible mix mode.
	// Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead.
	if(GetType() == MOD_TYPE_XM)
	{
		if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 22, 07, 19)
			&& m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 01, 04)
			&& GetMixLevels() == mixLevelsCompatible)
		{
			SetMixLevels(mixLevelsCompatibleFT2);
		}
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 25, 00, 07) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 25, 00, 00))
	{
		// Instrument plugins can now receive random volume variation.
		// For old instruments, disable volume swing in case there was no sample associated.
		for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
		{
			if(Instruments[i] != nullptr && Instruments[i]->nVolSwing != 0 && Instruments[i]->nMidiChannel != MidiNoChannel)
			{
				bool hasSample = false;
				for(size_t k = 0; k < CountOf(Instruments[k]->Keyboard); k++)
				{
					if(Instruments[i]->Keyboard[k] != 0)
					{
						hasSample = true;
						break;
					}
				}
				if(!hasSample)
				{
					Instruments[i]->nVolSwing = 0;
				}
			}
		}
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00))
	{
		for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr)
		{
			ModInstrument *ins = Instruments[i];
			// Even after fixing it in OpenMPT 1.18, instrument PPS was only half the depth.
			ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2;

			// OpenMPT 1.18 fixed the depth of random pan in compatible mode.
			// OpenMPT 1.26 fixes it in normal mode too.
			if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00))
			{
				ins->nPanSwing = (ins->nPanSwing + 3) / 4u;
			}
		}
	}

	Patterns.ForEachModCommand(UpgradePatternData(*this));

	// Convert compatibility flags
	// NOTE: Some of these version numbers are just approximations.
	// Sometimes a quirk flag is shared by several code locations which might have been fixed at different times.
	// Sometimes the quirk behaviour has been revised over time, in which case the first version that emulated the quirk enables it.
	struct PlayBehaviourVersion
	{
		PlayBehaviour behaviour;
		MptVersion::VersionNum version;
	};
	
	if(compatModeIT && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00))
	{
		// Pre-1.26: Detailed compatibility flags did not exist.
		static const PlayBehaviourVersion behaviours[] =
		{
			{ kTempoClamp,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kPerChannelGlobalVolSlide,		MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kPanOverride,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITInstrWithoutNote,				MAKE_VERSION_NUMERIC(1, 17, 02, 46) },
			{ kITVolColFinePortamento,			MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITArpeggio,						MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITOutOfRangeDelay,				MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITPortaMemoryShare,				MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITPatternLoopTargetReset,		MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITFT2PatternLoop,				MAKE_VERSION_NUMERIC(1, 17, 02, 49) },
			{ kITPingPongNoReset,				MAKE_VERSION_NUMERIC(1, 17, 02, 51) },
			{ kITEnvelopeReset,					MAKE_VERSION_NUMERIC(1, 17, 02, 51) },
			{ kITClearOldNoteAfterCut,			MAKE_VERSION_NUMERIC(1, 17, 02, 52) },
			{ kITVibratoTremoloPanbrello,		MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITTremor,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITRetrigger,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITMultiSampleBehaviour,			MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITPortaTargetReached,			MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITPatternLoopBreak,				MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITOffset,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITSwingBehaviour,				MAKE_VERSION_NUMERIC(1, 18, 00, 00) },
			{ kITNNAReset,						MAKE_VERSION_NUMERIC(1, 18, 00, 00) },
			{ kITSCxStopsSample,				MAKE_VERSION_NUMERIC(1, 18, 00, 01) },
			{ kITEnvelopePositionHandling,		MAKE_VERSION_NUMERIC(1, 18, 01, 00) },
			{ kITPortamentoInstrument,			MAKE_VERSION_NUMERIC(1, 19, 00, 01) },
			{ kITPingPongMode,					MAKE_VERSION_NUMERIC(1, 19, 00, 21) },
			{ kITRealNoteMapping,				MAKE_VERSION_NUMERIC(1, 19, 00, 30) },
			{ kITHighOffsetNoRetrig,			MAKE_VERSION_NUMERIC(1, 20, 00, 14) },
			{ kITFilterBehaviour,				MAKE_VERSION_NUMERIC(1, 20, 00, 35) },
			{ kITNoSurroundPan,					MAKE_VERSION_NUMERIC(1, 20, 00, 53) },
			{ kITShortSampleRetrig,				MAKE_VERSION_NUMERIC(1, 20, 00, 54) },
			{ kITPortaNoNote,					MAKE_VERSION_NUMERIC(1, 20, 00, 56) },
			{ kITDontResetNoteOffOnPorta,		MAKE_VERSION_NUMERIC(1, 20, 02, 06) },
			{ kITVolColMemory,					MAKE_VERSION_NUMERIC(1, 21, 01, 16) },
			{ kITPortamentoSwapResetsPos,		MAKE_VERSION_NUMERIC(1, 21, 01, 25) },
			{ kITEmptyNoteMapSlot,				MAKE_VERSION_NUMERIC(1, 21, 01, 25) },
			{ kITFirstTickHandling,				MAKE_VERSION_NUMERIC(1, 22, 07, 09) },
			{ kITSampleAndHoldPanbrello,		MAKE_VERSION_NUMERIC(1, 22, 07, 19) },
			{ kITClearPortaTarget,				MAKE_VERSION_NUMERIC(1, 23, 04, 03) },
			{ kITPanbrelloHold,					MAKE_VERSION_NUMERIC(1, 24, 01, 06) },
			{ kITPanningReset,					MAKE_VERSION_NUMERIC(1, 24, 01, 06) },
			{ kITPatternLoopWithJumps,			MAKE_VERSION_NUMERIC(1, 25, 00, 19) },
		};

		for(size_t i = 0; i < CountOf(behaviours); i++)
		{
			m_playBehaviour.set(behaviours[i].behaviour, (m_dwLastSavedWithVersion >= behaviours[i].version || m_dwLastSavedWithVersion == (behaviours[i].version & 0xFFFF0000)));
		}
	} else if(compatModeXM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00))
	{
		// Pre-1.26: Detailed compatibility flags did not exist.
		static const PlayBehaviourVersion behaviours[] =
		{
			{ kTempoClamp,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kPerChannelGlobalVolSlide,		MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kPanOverride,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kITFT2PatternLoop,				MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2Arpeggio,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2Retrigger,					MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2VolColVibrato,				MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2PortaNoNote,					MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2KeyOff,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2PanSlide,						MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2OffsetOutOfRange,				MAKE_VERSION_NUMERIC(1, 17, 03, 02) },
			{ kFT2RestrictXCommand,				MAKE_VERSION_NUMERIC(1, 18, 00, 00) },
			{ kFT2RetrigWithNoteDelay,			MAKE_VERSION_NUMERIC(1, 18, 00, 00) },
			{ kFT2SetPanEnvPos,					MAKE_VERSION_NUMERIC(1, 18, 00, 00) },
			{ kFT2PortaIgnoreInstr,				MAKE_VERSION_NUMERIC(1, 18, 00, 01) },
			{ kFT2VolColMemory,					MAKE_VERSION_NUMERIC(1, 18, 01, 00) },
			{ kFT2LoopE60Restart,				MAKE_VERSION_NUMERIC(1, 18, 02, 01) },
			{ kFT2ProcessSilentChannels,		MAKE_VERSION_NUMERIC(1, 18, 02, 01) },
			{ kFT2ReloadSampleSettings,			MAKE_VERSION_NUMERIC(1, 20, 00, 36) },
			{ kFT2PortaDelay,					MAKE_VERSION_NUMERIC(1, 20, 00, 40) },
			{ kFT2Transpose,					MAKE_VERSION_NUMERIC(1, 20, 00, 62) },
			{ kFT2PatternLoopWithJumps,			MAKE_VERSION_NUMERIC(1, 20, 00, 69) },
			{ kFT2PortaTargetNoReset,			MAKE_VERSION_NUMERIC(1, 20, 00, 69) },
			{ kFT2EnvelopeEscape,				MAKE_VERSION_NUMERIC(1, 20, 00, 77) },
			{ kFT2Tremor,						MAKE_VERSION_NUMERIC(1, 20, 01, 11) },
			{ kFT2OutOfRangeDelay,				MAKE_VERSION_NUMERIC(1, 20, 02, 02) },
			{ kFT2Periods,						MAKE_VERSION_NUMERIC(1, 22, 03, 01) },
			{ kFT2PanWithDelayedNoteOff,		MAKE_VERSION_NUMERIC(1, 22, 03, 02) },
			{ kFT2VolColDelay,					MAKE_VERSION_NUMERIC(1, 22, 07, 19) },
			{ kFT2FinetunePrecision,			MAKE_VERSION_NUMERIC(1, 22, 07, 19) },
		};

		for(size_t i = 0; i < CountOf(behaviours); i++)
		{
			m_playBehaviour.set(behaviours[i].behaviour, m_dwLastSavedWithVersion >= behaviours[i].version);
		}
	}
	
	if(GetType() == MOD_TYPE_IT)
	{
		// The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above.
		static const PlayBehaviourVersion behaviours[] =
		{
			{ kITInstrWithNoteOff,				MAKE_VERSION_NUMERIC(1, 26, 00, 01) },
			{ kITMultiSampleInstrumentNumber,	MAKE_VERSION_NUMERIC(1, 27, 00, 27) },
		};

		for(size_t i = 0; i < CountOf(behaviours); i++)
		{
			if(m_dwLastSavedWithVersion < (behaviours[i].version & 0xFFFF0000))
				m_playBehaviour.reset(behaviours[i].behaviour);
			// Full version information available, i.e. not compatibility-exported.
			if(m_dwLastSavedWithVersion > (behaviours[i].version & 0xFFFF0000) && m_dwLastSavedWithVersion < behaviours[i].version)
				m_playBehaviour.reset(behaviours[i].behaviour);
		}
	} else if(GetType() == MOD_TYPE_XM)
	{
		// The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above.
		static const PlayBehaviourVersion behaviours[] =
		{
			{ kFT2NoteOffFlags,					MAKE_VERSION_NUMERIC(1, 27, 00, 27) },
		};

		for(size_t i = 0; i < CountOf(behaviours); i++)
		{
			if(m_dwLastSavedWithVersion < behaviours[i].version)
				m_playBehaviour.reset(behaviours[i].behaviour);
		}
	} else if(GetType() == MOD_TYPE_S3M)
	{
		if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00))
			m_playBehaviour.reset(kST3NoMutedChannels);
		if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
			m_playBehaviour.reset(kST3EffectMemory);
		if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 00, 00))
			m_playBehaviour.reset(kST3PortaSampleChange);
		if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00))
			m_playBehaviour.reset(kST3VibratoMemory);
		if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00))
			m_playBehaviour.reset(kITPanbrelloHold);
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00))
	{
		// MPT 1.16 has a maximum tempo of 255.
		m_playBehaviour.set(kTempoClamp);
	} else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 20, 01, 03) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00))
	{
		// OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1.
		m_playBehaviour.set(kSlidesAtSpeed1);
	}

	if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 00, 00))
	{
		// No frequency slides in Hz before OpenMPT 1.24
		m_playBehaviour.reset(kHertzInLinearMode);
	} else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 24, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00) && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
	{
		// Frequency slides were always in Hz rather than periods in this version range.
		m_playBehaviour.set(kHertzInLinearMode);
	}
}
Beispiel #3
0
OPENMPT_NAMESPACE_BEGIN

// Version changelog:
// v1.03: - Relative unicode instrument paths instead of absolute ANSI paths
//        - Per-path variable string length
//        - Embedded samples are IT-compressed
//        (rev. 3249)
// v1.02: Explicitely updated format to use new instrument flags representation (rev. 483)
// v1.01: Added option to embed instrument headers


bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags)
//-------------------------------------------------------------------------
{
#ifndef MPT_EXTERNAL_SAMPLES
	// Doesn't really make sense to support this format when there's no support for external files...
	MPT_UNREFERENCED_PARAMETER(file);
	MPT_UNREFERENCED_PARAMETER(loadFlags);
	return false;
#else // MPT_EXTERNAL_SAMPLES
	
	enum ITPSongFlags
	{
		ITP_EMBEDMIDICFG	= 0x00001,	// Embed macros in file
		ITP_ITOLDEFFECTS	= 0x00004,	// Old Impulse Tracker effect implementations
		ITP_ITCOMPATGXX		= 0x00008,	// IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
		ITP_LINEARSLIDES	= 0x00010,	// Linear slides vs. Amiga slides
		ITP_EXFILTERRANGE	= 0x08000,	// Cutoff Filter has double frequency range (up to ~10Khz)
		ITP_ITPROJECT		= 0x20000,	// Is a project file
		ITP_ITPEMBEDIH		= 0x40000,	// Embed instrument headers in project file
	};

	uint32 version;
	FileReader::off_t size;

	file.Rewind();

	// Check file ID
	if(!file.CanRead(12 + 4 + 24 + 4)
		|| file.ReadUint32LE() != MAGIC4BE('.','i','t','p')	// Magic bytes
		|| (version = file.ReadUint32LE()) > 0x00000103		// Format version
		|| version < 0x00000100)
	{
		return false;
	} else if(loadFlags == onlyVerifyHeader)
	{
		return true;
	}

	InitializeGlobals(MOD_TYPE_IT);
	m_playBehaviour.reset();
	file.ReadString<mpt::String::maybeNullTerminated>(m_songName, file.ReadUint32LE());

	// Song comments
	m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);

	// Song global config
	const uint32 songFlags = file.ReadUint32LE();
	if(!(songFlags & ITP_ITPROJECT))
	{
		return false;
	}
	if(songFlags & ITP_EMBEDMIDICFG)	m_SongFlags.set(SONG_EMBEDMIDICFG);
	if(songFlags & ITP_ITOLDEFFECTS)	m_SongFlags.set(SONG_ITOLDEFFECTS);
	if(songFlags & ITP_ITCOMPATGXX)		m_SongFlags.set(SONG_ITCOMPATGXX);
	if(songFlags & ITP_LINEARSLIDES)	m_SongFlags.set(SONG_LINEARSLIDES);
	if(songFlags & ITP_EXFILTERRANGE)	m_SongFlags.set(SONG_EXFILTERRANGE);

	m_nDefaultGlobalVolume = file.ReadUint32LE();
	m_nSamplePreAmp = file.ReadUint32LE();
	m_nDefaultSpeed = std::max(uint32(1), file.ReadUint32LE());
	m_nDefaultTempo.Set(std::max(uint32(32), file.ReadUint32LE()));
	m_nChannels = static_cast<CHANNELINDEX>(file.ReadUint32LE());
	if(m_nChannels == 0 || m_nChannels > MAX_BASECHANNELS)
	{
		return false;
	}

	// channel name string length (=MAX_CHANNELNAME)
	size = file.ReadUint32LE();

	// Channels' data
	for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
	{
		ChnSettings[chn].nPan = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(256));
		ChnSettings[chn].dwFlags.reset();
		uint32 flags = file.ReadUint32LE();
		if(flags & 0x100) ChnSettings[chn].dwFlags.set(CHN_MUTE);
		if(flags & 0x800) ChnSettings[chn].dwFlags.set(CHN_SURROUND);
		ChnSettings[chn].nVolume = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(64));
		file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, size);
	}

	// Song mix plugins
	{
		FileReader plugChunk = file.ReadChunk(file.ReadUint32LE());
		LoadMixPlugins(plugChunk);
	}

	// MIDI Macro config
	file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE());
	m_MidiCfg.Sanitize();

	// Song Instruments
	m_nInstruments = static_cast<INSTRUMENTINDEX>(file.ReadUint32LE());
	if(m_nInstruments >= MAX_INSTRUMENTS)
	{
		return false;
	}

	// Instruments' paths
	if(version <= 0x00000102)
	{
		size = file.ReadUint32LE();	// path string length
	}

	std::vector<mpt::PathString> instrPaths(GetNumInstruments());
	for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
	{
		if(version > 0x00000102)
		{
			size = file.ReadUint32LE();	// path string length
		}
		std::string path;
		file.ReadString<mpt::String::maybeNullTerminated>(path, size);
		if(version <= 0x00000102)
		{
			instrPaths[ins] = mpt::PathString::FromLocaleSilent(path);
		} else
		{
			instrPaths[ins] = mpt::PathString::FromUTF8(path);
		}
	}

	// Song Orders
	size = file.ReadUint32LE();
	Order.ReadAsByte(file, size, size, 0xFF, 0xFE);

	// Song Patterns
	const PATTERNINDEX numPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
	const PATTERNINDEX numNamedPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
	size_t patNameLen = file.ReadUint32LE();	// Size of each pattern name
	FileReader pattNames = file.ReadChunk(numNamedPats * patNameLen);

	// modcommand data length
	size = file.ReadUint32LE();
	if(size != 6)
	{
		return false;
	}

	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
	{
		const ROWINDEX numRows = file.ReadUint32LE();
		FileReader patternChunk = file.ReadChunk(numRows * size * GetNumChannels());

		// Allocate pattern
		if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows))
		{
			pattNames.Skip(patNameLen);
			continue;
		}

		if(pat < numNamedPats)
		{
			char patName[32];
			pattNames.ReadString<mpt::String::maybeNullTerminated>(patName, patNameLen);
			Patterns[pat].SetName(patName);
		}

		// Pattern data
		size_t numCommands = GetNumChannels() * numRows;

		if(patternChunk.CanRead(sizeof(MODCOMMAND_ORIGINAL) * numCommands))
		{
			ModCommand *target = Patterns[pat].GetpModCommand(0, 0);
			while(numCommands-- != 0)
			{
				STATIC_ASSERT(sizeof(MODCOMMAND_ORIGINAL) == 6);
				MODCOMMAND_ORIGINAL data;
				patternChunk.ReadStruct(data);
				if(data.command >= MAX_EFFECTS) data.command = CMD_NONE;
				if(data.volcmd >= MAX_VOLCMDS) data.volcmd = VOLCMD_NONE;
				if(data.note > NOTE_MAX && data.note < NOTE_MIN_SPECIAL) data.note = NOTE_NONE;
				*(target++) = data;
			}
		}
	}

	// Load embedded samples

	// Read original number of samples
	m_nSamples = static_cast<SAMPLEINDEX>(file.ReadUint32LE());
	LimitMax(m_nSamples, SAMPLEINDEX(MAX_SAMPLES - 1));

	// Read number of embedded samples
	uint32 embeddedSamples = file.ReadUint32LE();

	// Read samples
	for(uint32 smp = 0; smp < embeddedSamples; smp++)
	{
		SAMPLEINDEX realSample = static_cast<SAMPLEINDEX>(file.ReadUint32LE());
		ITSample sampleHeader;
		file.ReadConvertEndianness(sampleHeader);
		FileReader sampleData = file.ReadChunk(file.ReadUint32LE());

		if(realSample >= 1 && realSample <= GetNumSamples() && !memcmp(sampleHeader.id, "IMPS", 4) && (loadFlags & loadSampleData))
		{
			sampleHeader.ConvertToMPT(Samples[realSample]);
			mpt::String::Read<mpt::String::nullTerminated>(m_szNames[realSample], sampleHeader.name);

			// Read sample data
			sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData);
		}
	}

	// Load instruments
	for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
	{
		if(instrPaths[ins].empty())
			continue;

		if(!file.GetFileName().empty())
		{
			instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(file.GetFileName().GetPath());
		}
#ifdef MODPLUG_TRACKER
		else if(GetpModDoc() != nullptr)
		{
			instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath());
		}
#endif // MODPLUG_TRACKER

		InputFile f(instrPaths[ins]);
		FileReader file = GetFileReader(f);
		if(!ReadInstrumentFromFile(ins + 1, file, true))
		{
			AddToLog(LogWarning, MPT_USTRING("Unable to open instrument: ") + instrPaths[ins].ToUnicode());
		}
	}

	// Extra info data
	uint32 code = file.ReadUint32LE();

	// Embed instruments' header [v1.01]
	if(version >= 0x00000101 && (songFlags & ITP_ITPEMBEDIH) && code == MAGIC4BE('E', 'B', 'I', 'H'))
	{
		code = file.ReadUint32LE();

		INSTRUMENTINDEX ins = 1;
		while(ins <= GetNumInstruments() && file.CanRead(4))
		{
			if(code == MAGIC4BE('M', 'P', 'T', 'S'))
			{
				break;
			} else if(code == MAGIC4BE('S', 'E', 'P', '@') || code == MAGIC4BE('M', 'P', 'T', 'X'))
			{
				// jump code - switch to next instrument
				ins++;
			} else
			{
				ReadExtendedInstrumentProperty(Instruments[ins], code, file);
			}

			code = file.ReadUint32LE();
		}
	}

	// Song extensions
	if(code == MAGIC4BE('M', 'P', 'T', 'S'))
	{
		file.SkipBack(4);
		LoadExtendedSongProperties(file);
	}

	m_nMaxPeriod = 0xF000;
	m_nMinPeriod = 8;

	// Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set.
	if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1,20,01,09) && !m_SongFlags[SONG_EMBEDMIDICFG])
	{
		m_MidiCfg.Reset();
	} else if(!m_MidiCfg.IsMacroDefaultSetupUsed())
	{
		m_SongFlags.set(SONG_EMBEDMIDICFG);
	}

	m_madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion);

	return true;
#endif // MPT_EXTERNAL_SAMPLES
}
Beispiel #4
0
bool module_renderer::ReadITProject(const uint8_t * lpStream, const uint32_t dwMemLength)
//-----------------------------------------------------------------------
{
    UINT i,n,nsmp;
    uint32_t id,len,size;
    uint32_t dwMemPos = 0;
    uint32_t version;

    ASSERT_CAN_READ(12);

    // Check file ID

    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    if(id != ITP_FILE_ID) return false;
    dwMemPos += sizeof(uint32_t);

    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    version = id;
    dwMemPos += sizeof(uint32_t);

    // bad_max supported version
    if(version > ITP_VERSION)
    {
        return false;
    }

    m_nType = MOD_TYPE_IT;

    // Song name

    // name string length
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    len = id;
    dwMemPos += sizeof(uint32_t);

    // name string
    ASSERT_CAN_READ(len);
    if (len <= MAX_SAMPLENAME)
    {
        assign_without_padding(this->song_name, reinterpret_cast<const char *>(lpStream + dwMemPos), len);
        dwMemPos += len;
    }
    else return false;

    // Song comments

    // comment string length
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    dwMemPos += sizeof(uint32_t);
    if(id > UINT16_MAX) return false;

    // allocate and copy comment string
    ASSERT_CAN_READ(id);
    if(id > 0)
    {
        ReadMessage(lpStream + dwMemPos, id - 1, leCR);
    }
    dwMemPos += id;

    // Song global config
    ASSERT_CAN_READ(5*4);

    // m_dwSongFlags
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_dwSongFlags = (id & SONG_FILE_FLAGS);
    dwMemPos += sizeof(uint32_t);

    if(!(m_dwSongFlags & SONG_ITPROJECT)) return false;

    // m_nDefaultGlobalVolume
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nDefaultGlobalVolume = id;
    dwMemPos += sizeof(uint32_t);

    // m_nSamplePreAmp
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nSamplePreAmp = id;
    dwMemPos += sizeof(uint32_t);

    // m_nDefaultSpeed
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nDefaultSpeed = id;
    dwMemPos += sizeof(uint32_t);

    // m_nDefaultTempo
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nDefaultTempo = id;
    dwMemPos += sizeof(uint32_t);

    // Song channels data
    ASSERT_CAN_READ(2*4);

    // m_nChannels
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nChannels = (modplug::tracker::chnindex_t)id;
    dwMemPos += sizeof(uint32_t);
    if(m_nChannels > 127) return false;

    // channel name string length (=MAX_CHANNELNAME)
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    len = id;
    dwMemPos += sizeof(uint32_t);
    if(len > MAX_CHANNELNAME) return false;

    // Channels' data
    for(i=0; i<m_nChannels; i++){
        ASSERT_CAN_READ(3*4 + len);

        // ChnSettings[i].nPan
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        ChnSettings[i].nPan = id;
        dwMemPos += sizeof(uint32_t);

        // ChnSettings[i].dwFlags
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        ChnSettings[i].dwFlags = id;
        dwMemPos += sizeof(uint32_t);

        // ChnSettings[i].nVolume
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        ChnSettings[i].nVolume = id;
        dwMemPos += sizeof(uint32_t);

        // ChnSettings[i].szName
        memcpy(&ChnSettings[i].szName[0],lpStream+dwMemPos,len);
        SetNullTerminator(ChnSettings[i].szName);
        dwMemPos += len;
    }

    // Song mix plugins
    // size of mix plugins data
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    dwMemPos += sizeof(uint32_t);

    // mix plugins
    ASSERT_CAN_READ(id);
    dwMemPos += LoadMixPlugins(lpStream+dwMemPos, id);

    // Song midi config

    // midi cfg data length
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    dwMemPos += sizeof(uint32_t);

    // midi cfg
    ASSERT_CAN_READ(id);
    if (id <= sizeof(m_MidiCfg))
    {
        memcpy(&m_MidiCfg, lpStream + dwMemPos, id);
        SanitizeMacros();
        dwMemPos += id;
    }

    // Song Instruments

    // m_nInstruments
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    m_nInstruments = (modplug::tracker::instrumentindex_t)id;
    if(m_nInstruments > MAX_INSTRUMENTS) return false;
    dwMemPos += sizeof(uint32_t);

    // path string length (=_MAX_PATH)
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    len = id;
    if(len > _MAX_PATH) return false;
    dwMemPos += sizeof(uint32_t);

    // instruments' paths
    for(i=0; i<m_nInstruments; i++){
        ASSERT_CAN_READ(len);
        memcpy(&m_szInstrumentPath[i][0],lpStream+dwMemPos,len);
        SetNullTerminator(m_szInstrumentPath[i]);
        dwMemPos += len;
    }

    // Song Orders

    // size of order array (=MAX_ORDERS)
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    size = id;
    if(size > MAX_ORDERS) return false;
    dwMemPos += sizeof(uint32_t);

    // order data
    ASSERT_CAN_READ(size);
    Order.ReadAsByte(lpStream+dwMemPos, size, dwMemLength-dwMemPos);
    dwMemPos += size;



    // Song Patterns

    ASSERT_CAN_READ(3*4);
    // number of patterns (=MAX_PATTERNS)
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    size = id;
    dwMemPos += sizeof(uint32_t);
    if(size > MAX_PATTERNS) return false;

    // m_nPatternNames
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    const modplug::tracker::patternindex_t numNamedPats = id;
    dwMemPos += sizeof(uint32_t);

    // pattern name string length (=MAX_PATTERNNAME)
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    const uint32_t patNameLen = id;
    dwMemPos += sizeof(uint32_t);

    // m_lpszPatternNames
    ASSERT_CAN_READ(numNamedPats * patNameLen);
    char *patNames = (char *)(lpStream + dwMemPos);
    dwMemPos += numNamedPats * patNameLen;

    // modcommand data length
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    n = id;
    if(n != 6) return false;
    dwMemPos += sizeof(uint32_t);

    for(modplug::tracker::patternindex_t npat=0; npat<size; npat++)
    {
        // Patterns[npat].GetNumRows()
        ASSERT_CAN_READ(4);
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        if(id > MAX_PATTERN_ROWS) return false;
        const modplug::tracker::rowindex_t nRows = id;
        dwMemPos += sizeof(uint32_t);

        // Try to allocate & read only sized patterns
        if(nRows)
        {

            // Allocate pattern
            if(Patterns.Insert(npat, nRows))
            {
                dwMemPos += m_nChannels * Patterns[npat].GetNumRows() * n;
                continue;
            }
            if(npat < numNamedPats && patNameLen > 0)
            {
                Patterns[npat].SetName(patNames, patNameLen);
                patNames += patNameLen;
            }

            // Pattern data
            long datasize = m_nChannels * Patterns[npat].GetNumRows() * n;
            //if (streamPos+datasize<=dwMemLength) {
            if(Patterns[npat].ReadITPdata(lpStream, dwMemPos, datasize, dwMemLength))
            {
                ErrorBox(IDS_ERR_FILEOPEN, NULL);
                return false;
            }
            //memcpy(Patterns[npat],lpStream+streamPos,datasize);
            //streamPos += datasize;
            //}
        }
    }

    // Load embeded samples

    ITSAMPLESTRUCT pis;

    // Read original number of samples
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    if(id > MAX_SAMPLES) return false;
    m_nSamples = (modplug::tracker::sampleindex_t)id;
    dwMemPos += sizeof(uint32_t);

    // Read number of embeded samples
    ASSERT_CAN_READ(4);
    memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
    if(id > MAX_SAMPLES) return false;
    n = id;
    dwMemPos += sizeof(uint32_t);

    // Read samples
    for(i=0; i<n; i++){

        ASSERT_CAN_READ(4 + sizeof(ITSAMPLESTRUCT) + 4);

        // Sample id number
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        nsmp = id;
        dwMemPos += sizeof(uint32_t);

        if(nsmp < 1 || nsmp >= MAX_SAMPLES)
            return false;

        // Sample struct
        memcpy(&pis,lpStream+dwMemPos,sizeof(ITSAMPLESTRUCT));
        dwMemPos += sizeof(ITSAMPLESTRUCT);

        // Sample length
        memcpy(&id,lpStream+dwMemPos,sizeof(uint32_t));
        len = id;
        dwMemPos += sizeof(uint32_t);
        if(dwMemPos >= dwMemLength || len > dwMemLength - dwMemPos) return false;

        // Copy sample struct data (ut-oh... this code looks very familiar!)
        if(pis.id == LittleEndian(IT_IMPS))
        {
            modsample_t *pSmp = &Samples[nsmp];
            memcpy(pSmp->legacy_filename, pis.filename, 12);
            pSmp->flags = 0;
            pSmp->length = 0;
            pSmp->loop_start = pis.loopbegin;
            pSmp->loop_end = pis.loopend;
            pSmp->sustain_start = pis.susloopbegin;
            pSmp->sustain_end = pis.susloopend;
            pSmp->c5_samplerate = pis.C5Speed;
            if(!pSmp->c5_samplerate) pSmp->c5_samplerate = 8363;
            if(pis.C5Speed < 256) pSmp->c5_samplerate = 256;
            pSmp->default_volume = pis.vol << 2;
            if(pSmp->default_volume > 256) pSmp->default_volume = 256;
            pSmp->global_volume = pis.gvl;
            if(pSmp->global_volume > 64) pSmp->global_volume = 64;
            if(pis.flags & 0x10) pSmp->flags |= CHN_LOOP;
            if(pis.flags & 0x20) pSmp->flags |= CHN_SUSTAINLOOP;
            if(pis.flags & 0x40) pSmp->flags |= CHN_PINGPONGLOOP;
            if(pis.flags & 0x80) pSmp->flags |= CHN_PINGPONGSUSTAIN;
            pSmp->default_pan = (pis.dfp & 0x7F) << 2;
            if(pSmp->default_pan > 256) pSmp->default_pan = 256;
            if(pis.dfp & 0x80) pSmp->flags |= CHN_PANNING;
            pSmp->vibrato_type = autovibit2xm[pis.vit & 7];
            pSmp->vibrato_rate = pis.vis;
            pSmp->vibrato_depth = pis.vid & 0x7F;
            pSmp->vibrato_sweep = pis.vir;
            if(pis.length){
                pSmp->length = pis.length;
                if (pSmp->length > MAX_SAMPLE_LENGTH) pSmp->length = MAX_SAMPLE_LENGTH;
                UINT flags = (pis.cvt & 1) ? RS_PCM8S : RS_PCM8U;
                if (pis.flags & 2){
                    flags += 5;
                    if (pis.flags & 4) flags |= RSF_STEREO;
                    pSmp->flags |= CHN_16BIT;
                }
                else{
                    if (pis.flags & 4) flags |= RSF_STEREO;
                }
                // Read sample data
                ReadSample(&Samples[nsmp], flags, (LPSTR)(lpStream+dwMemPos), len);
                dwMemPos += len;
                memcpy(m_szNames[nsmp], pis.name, 26);
            }
        }
    }

    // Load instruments

    CMappedFile f;
    LPBYTE lpFile;

    for(modplug::tracker::instrumentindex_t i = 0; i < m_nInstruments; i++)
    {

        if(m_szInstrumentPath[i][0] == '\0' || !f.Open(m_szInstrumentPath[i])) continue;

        len = f.GetLength();
        lpFile = f.Lock(len);
        if(!lpFile) { f.Close(); continue; }

        ReadInstrumentFromFile(i+1, lpFile, len);
        f.Unlock();
        f.Close();
    }

    // Extra info data

    __int32 fcode = 0;
    const uint8_t * ptr = lpStream + bad_min(dwMemPos, dwMemLength);

    if (dwMemPos <= dwMemLength - 4) {
        fcode = (*((__int32 *)ptr));
    }

    // Embed instruments' header [v1.01]
    if(version >= 0x00000101 && m_dwSongFlags & SONG_ITPEMBEDIH && fcode == 'EBIH')
    {
        // jump embeded instrument header tag
        ptr += sizeof(__int32);

        // set first instrument's header as current
        i = 1;

        // parse file
        while( uintptr_t(ptr - lpStream) <= dwMemLength - 4 && i <= m_nInstruments )
        {

            fcode = (*((__int32 *)ptr));                    // read field code

            switch( fcode )
            {
            case 'MPTS': goto mpts; //:)            // reached end of instrument headers
            case 'SEP@': case 'MPTX':
                ptr += sizeof(__int32);                    // jump code
                i++;                                                    // switch to next instrument
                break;

            default:
                ptr += sizeof(__int32);                    // jump field code
                ReadExtendedInstrumentProperty(Instruments[i], fcode, ptr, lpStream + dwMemLength);
                break;
            }
        }
    }

    //HACK: if we fail on i <= m_nInstruments above, arrive here without having set fcode as appropriate,
    //      hence the code duplication.
    if ( (uintptr_t)(ptr - lpStream) <= dwMemLength - 4 )
    {
        fcode = (*((__int32 *)ptr));
    }

    // Song extensions
mpts:
    if( fcode == 'MPTS' )
        LoadExtendedSongProperties(MOD_TYPE_IT, ptr, lpStream, dwMemLength);

    m_nMaxPeriod = 0xF000;
    m_nMinPeriod = 8;

    if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 2, 50))
    {
        SetModFlag(MSF_COMPATIBLE_PLAY, false);
        SetModFlag(MSF_MIDICC_BUGEMULATION, true);
        SetModFlag(MSF_OLDVOLSWING, true);
    }

    return true;
}
Beispiel #5
0
bool CSoundFile::ReadITQ(FileReader &file, ModLoadingFlags loadFlags)
//------------------------------------------------------------------
{
	file.Rewind();

	ITFileHeader fileHeader;
	if(!file.ReadConvertEndianness(fileHeader)
		|| (memcmp(fileHeader.id, "ITQM", 4))
		|| fileHeader.insnum > 0xFF
		|| fileHeader.smpnum >= MAX_SAMPLES
		|| !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4))
	{
		return false;
	} else if(loadFlags == onlyVerifyHeader)
	{
		return true;
	}

	InitializeGlobals();

	bool interpretModPlugMade = false;

	// OpenMPT crap at the end of file
	file.Seek(file.GetLength() - 4);
	size_t mptStartPos = file.ReadUint32LE();
	if(mptStartPos >= file.GetLength() || mptStartPos < 0x100)
	{
		mptStartPos = file.GetLength();
	}

	if(!memcmp(fileHeader.id, "tpm.", 4))
	{
		// Legacy MPTM files (old 1.17.02.xx releases)
		ChangeModTypeTo(MOD_TYPE_MPT);
	} else
	{
		if(mptStartPos <= file.GetLength() - 3 && fileHeader.cwtv > 0x888 && fileHeader.cwtv <= 0xFFF)
		{
			file.Seek(mptStartPos);
			ChangeModTypeTo(file.ReadMagic("228") ? MOD_TYPE_MPT : MOD_TYPE_IT);
		} else
		{
			ChangeModTypeTo(MOD_TYPE_IT);
		}

		if(GetType() == MOD_TYPE_IT)
		{
			// Which tracker was used to made this?
			if((fileHeader.cwtv & 0xF000) == 0x5000)
			{
				// OpenMPT Version number (Major.Minor)
				// This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used.
				m_dwLastSavedWithVersion = (fileHeader.cwtv & 0x0FFF) << 16;
				if(!memcmp(fileHeader.reserved, "OMPT", 4))
					interpretModPlugMade = true;
			} else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888)
			{
				// OpenMPT 1.17 and 1.18 (raped IT format)
				// Exact version number will be determined later.
				interpretModPlugMade = true;
			} else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4))
			{
				if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != NULL)
				{
					// ModPlug Tracker 1.16 (semi-raped IT format)
					m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00);
					madeWithTracker = "ModPlug tracker 1.09 - 1.16";
				} else
				{
					// OpenMPT 1.17 disguised as this in compatible mode,
					// but never writes 0xFF in the pan map for unused channels (which is an invalid value).
					m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00);
					madeWithTracker = "OpenMPT 1.17 (compatibility export)";
				}
				interpretModPlugMade = true;
			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4))
			{
				// ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart
				m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00);
				madeWithTracker = "ModPlug tracker b3.3 - 1.09";
				interpretModPlugMade = true;
			}
		} else // case: type == MOD_TYPE_MPT
		{
			if (fileHeader.cwtv >= verMptFileVerLoadLimit)
			{
				AddToLog(str_LoadingIncompatibleVersion);
				return false;
			}
			else if (fileHeader.cwtv > verMptFileVer)
			{
				AddToLog(str_LoadingMoreRecentVersion);
			}
		}
	}

	if(GetType() == MOD_TYPE_IT) mptStartPos = file.GetLength();

	// Read row highlights
	if((fileHeader.special & ITFileHeader::embedPatternHighlights))
	{
		// MPT 1.09, 1.07 and most likely other old MPT versions leave this blank (0/0), but have the "special" flag set.
		// Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here.
		// Thus, we will just ignore those old versions.
		if(m_dwLastSavedWithVersion == 0 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02))
		{
			m_nDefaultRowsPerBeat = fileHeader.highlight_minor;
			m_nDefaultRowsPerMeasure = fileHeader.highlight_major;
		}
#ifdef _DEBUG
		if((fileHeader.highlight_minor | fileHeader.highlight_major) == 0)
		{
			Log("IT Header: Row highlight is 0");
		}
#endif
	}

	m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & ITFileHeader::linearSlides) != 0);
	m_SongFlags.set(SONG_ITOLDEFFECTS, (fileHeader.flags & ITFileHeader::itOldEffects) != 0);
	m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0);
	m_SongFlags.set(SONG_EMBEDMIDICFG, (fileHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (fileHeader.special & ITFileHeader::embedMIDIConfiguration));
	m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0);

	mpt::String::Read<mpt::String::spacePadded>(songName, fileHeader.songname);

	// Global Volume
	m_nDefaultGlobalVolume = fileHeader.globalvol << 1;
	if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
	if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed;
	m_nDefaultTempo = std::max(uint8(32), fileHeader.tempo); // Tempo 31 is possible. due to conflicts with the rest of the engine, let's just clamp it to 32.
	m_nSamplePreAmp = std::min(fileHeader.mv, uint8(128));

	// Reading Channels Pan Positions
	for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF)
	{
		ChnSettings[i].Reset();
		ChnSettings[i].nVolume = Clamp(fileHeader.chnvol[i], uint8(0), uint8(64));
		if(fileHeader.chnpan[i] & 0x80) ChnSettings[i].dwFlags.set(CHN_MUTE);
		uint8 n = fileHeader.chnpan[i] & 0x7F;
		if(n <= 64) ChnSettings[i].nPan = n * 4;
		if(n == 100) ChnSettings[i].dwFlags.set(CHN_SURROUND);
	}

	// Reading orders
	file.Seek(sizeof(ITFileHeader));
	if(GetType() == MOD_TYPE_IT)
	{
		Order.ReadAsByte(file, fileHeader.ordnum);
	} else
	{
		if(fileHeader.cwtv > 0x88A && fileHeader.cwtv <= 0x88D)
		{
			Order.Deserialize(file);
		} else
		{
			Order.ReadAsByte(file, fileHeader.ordnum);
			// Replacing 0xFF and 0xFE with new corresponding indexes
			Order.Replace(0xFE, Order.GetIgnoreIndex());
			Order.Replace(0xFF, Order.GetInvalidPatIndex());
		}
	}

	// Reading instrument, sample and pattern offsets
	std::vector<uint32> insPos, smpPos, patPos;
	file.ReadVectorLE(insPos, fileHeader.insnum);
	file.ReadVectorLE(smpPos, fileHeader.smpnum);
	file.ReadVectorLE(patPos, fileHeader.patnum);

	// Find the first parapointer.
	// This is used for finding out whether the edit history is actually stored in the file or not,
	// as some early versions of Schism Tracker set the history flag, but didn't save anything.
	// We will consider the history invalid if it ends after the first parapointer.
	uint32 minPtr = Util::MaxValueOfType(minPtr);
	for(uint16 n = 0; n < fileHeader.insnum; n++)
	{
		if(insPos[n] > 0)
		{
			minPtr = std::min(minPtr, insPos[n]);
		}
	}

	for(uint16 n = 0; n < fileHeader.smpnum; n++)
	{
		if(smpPos[n] > 0)
		{
			minPtr = std::min(minPtr, smpPos[n]);
		}
	}

	for(uint16 n = 0; n < fileHeader.patnum; n++)
	{
		if(patPos[n] > 0)
		{
			minPtr = std::min(minPtr, patPos[n]);
		}
	}

	if(fileHeader.special & ITFileHeader::embedSongMessage)
	{
		minPtr = std::min(minPtr, fileHeader.msgoffset);
	}

	// Reading IT Edit History Info
	// This is only supposed to be present if bit 1 of the special flags is set.
	// However, old versions of Schism and probably other trackers always set this bit
	// even if they don't write the edit history count. So we have to filter this out...
	// This is done by looking at the parapointers. If the history data end after
	// the first parapointer, we assume that it's actually no history data.
	if(fileHeader.special & ITFileHeader::embedEditHistory)
	{
		const uint16 nflt = file.ReadUint16LE();

		if(file.CanRead(nflt * sizeof(ITHistoryStruct)) && file.GetPosition() + nflt * sizeof(ITHistoryStruct) <= minPtr)
		{
			m_FileHistory.reserve(nflt);
			for(size_t n = 0; n < nflt; n++)
			{
				FileHistory mptHistory;
				ITHistoryStruct itHistory;
				file.ReadConvertEndianness(itHistory);
				itHistory.ConvertToMPT(mptHistory);
				m_FileHistory.push_back(mptHistory);
			}
		} else
		{
			// Oops, we were not supposed to read this.
			file.SkipBack(2);
		}
	} else if(fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.cmwt == 0x0214 && fileHeader.cwtv == 0x0214 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && (fileHeader.special & (ITFileHeader::embedEditHistory | ITFileHeader::embedPatternHighlights)) == 0)
	{
		// Another non-conforming application is unmo3 < v2.4.0.1, which doesn't set the special bit
		// at all, but still writes the two edit history length bytes (zeroes)...
		if(file.ReadUint16LE() != 0)
		{
			// These were not zero bytes -> We're in the wrong place!
			file.SkipBack(2);
			madeWithTracker = "UNMO3";
		}
	}

	// Reading MIDI Output & Macros
	if(m_SongFlags[SONG_EMBEDMIDICFG] && file.Read(m_MidiCfg))
	{
			m_MidiCfg.Sanitize();
	}

	// Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns.
	if(fileHeader.cwtv < 0x0214)
	{
		MemsetZero(m_MidiCfg.szMidiSFXExt);
		MemsetZero(m_MidiCfg.szMidiZXXExt);
		m_SongFlags.set(SONG_EMBEDMIDICFG);
	}

	if(file.ReadMagic("MODU"))
	{
		madeWithTracker = "BeRoTracker";
	}

	// Read pattern names: "PNAM"
	FileReader patNames;
	if(file.ReadMagic("PNAM"))
	{
		patNames = file.GetChunk(file.ReadUint32LE());
	}

	m_nChannels = GetModSpecifications().channelsMin;
	// Read channel names: "CNAM"
	if(file.ReadMagic("CNAM"))
	{
		FileReader chnNames = file.GetChunk(file.ReadUint32LE());
		const CHANNELINDEX readChns = std::min(MAX_BASECHANNELS, static_cast<CHANNELINDEX>(chnNames.GetLength() / MAX_CHANNELNAME));
		m_nChannels = readChns;

		for(CHANNELINDEX i = 0; i < readChns; i++)
		{
			chnNames.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[i].szName, MAX_CHANNELNAME);
		}
	}

	// Read mix plugins information
	if(file.CanRead(9))
	{
		LoadMixPlugins(file);
	}

	// Read Song Message
	if(fileHeader.special & ITFileHeader::embedSongMessage)
	{
		if(fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset))
		{
			// Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do...
			// if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0214 && itHeader.reserved == ITFileHeader::chibiMagic) --> Chibi detected.
			// But we'll just use autodetection here:
			songMessage.Read(file, fileHeader.msglength, SongMessage::leAutodetect);
		}
	}

	// Reading Instruments
	m_nInstruments = 0;
	if(fileHeader.flags & ITFileHeader::instrumentMode)
	{
		m_nInstruments = std::min(fileHeader.insnum, INSTRUMENTINDEX(MAX_INSTRUMENTS - 1));
	}
	for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++)
	{
		if(insPos[i] > 0 && file.Seek(insPos[i]) && file.CanRead(fileHeader.cmwt < 0x200 ? sizeof(ITOldInstrument) : sizeof(ITInstrument)))
		{
			ModInstrument *instrument = AllocateInstrument(i + 1);
			if(instrument != nullptr)
			{
				ITInstrToMPT(file, *instrument, fileHeader.cmwt);
				// MIDI Pitch Wheel Depth is a global setting in IT. Apply it to all instruments.
				instrument->midiPWD = fileHeader.pwd;
			}
		}
	}

	// In order to properly compute the position, in file, of eventual extended settings
	// such as "attack" we need to keep the "real" size of the last sample as those extra
	// setting will follow this sample in the file
	FileReader::off_t lastSampleOffset = 0;
	if(fileHeader.smpnum > 0)
	{
		lastSampleOffset = smpPos[fileHeader.smpnum - 1] + sizeof(ITSample);
	}

	//// #ITQ

	// Reading Samples
	m_nSamples = std::min(fileHeader.smpnum, SAMPLEINDEX(MAX_SAMPLES - 1));
	size_t nbytes = 0; // size of sample data in file

	for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++)
	{
		ITQSample sampleHeader;
		if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadConvertEndianness(sampleHeader))
		{
			if(!memcmp(sampleHeader.id, "ITQS", 4))
			{
				size_t sampleOffset = sampleHeader.ConvertToMPT(Samples[i + 1]);

				mpt::String::Read<mpt::String::spacePadded>(m_szNames[i + 1], sampleHeader.name);

				if((loadFlags & loadSampleData) && file.Seek(sampleOffset))
				{
					Samples[i+1].originalSize = sampleHeader.nbytes;
					sampleHeader.GetSampleFormatITQ(fileHeader.cwtv).ReadSample(Samples[i + 1], file);
					lastSampleOffset = std::max(lastSampleOffset, file.GetPosition());
				}
			}
		}
	}
	m_nSamples = std::max(SAMPLEINDEX(1), GetNumSamples());

	m_nMinPeriod = 8;
	m_nMaxPeriod = 0xF000;

	PATTERNINDEX numPats = std::min(static_cast<PATTERNINDEX>(patPos.size()), GetModSpecifications().patternsMax);

	if(numPats != patPos.size())
	{
		// Hack: Notify user here if file contains more patterns than what can be read.
		AddToLog(mpt::String::Print(str_PatternSetTruncationNote, patPos.size(), numPats));
	}

	if(!(loadFlags & loadPatternData))
	{
		numPats = 0;
	}

	// Checking for number of used channels, which is not explicitely specified in the file.
	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
	{
		if(patPos[pat] == 0 || !file.Seek(patPos[pat]))
			continue;

		uint16 len = file.ReadUint16LE();
		ROWINDEX numRows = file.ReadUint16LE();

		if(numRows < GetModSpecifications().patternRowsMin
			|| numRows > GetModSpecifications().patternRowsMax
			|| !file.Skip(4))
			continue;

		FileReader patternData = file.GetChunk(len);
		ROWINDEX row = 0;
		std::vector<uint8> chnMask(GetNumChannels());

		while(row < numRows && patternData.AreBytesLeft())
		{
			uint8 b = patternData.ReadUint8();
			if(!b)
			{
				row++;
				continue;
			}

			CHANNELINDEX ch = (b & IT_bitmask_patternChanField_c);   // 0x7f We have some data grab a byte keeping only 7 bits
			if(ch)
			{
				ch = (ch - 1);// & IT_bitmask_patternChanMask_c;   // 0x3f mask of the byte again, keeping only 6 bits
			}

			if(ch >= chnMask.size())
			{
				chnMask.resize(ch + 1, 0);
			}

			if(b & IT_bitmask_patternChanEnabled_c)            // 0x80 check if the upper bit is enabled.
			{
				chnMask[ch] = patternData.ReadUint8();       // set the channel mask for this channel.
			}
			// Channel used
			if(chnMask[ch] & 0x0F)         // if this channel is used set m_nChannels
			{
				if(ch >= GetNumChannels() && ch < MAX_BASECHANNELS)
				{
					m_nChannels = ch + 1;
				}
			}
			// Now we actually update the pattern-row entry the note,instrument etc.
			// Note
			if(chnMask[ch] & 1) patternData.Skip(1);
			// Instrument
			if(chnMask[ch] & 2) patternData.Skip(1);
			// Volume
			if(chnMask[ch] & 4) patternData.Skip(1);
			// Effect
			if(chnMask[ch] & 8) patternData.Skip(2);
		}
	}

	// Compute extra instruments settings position
	if(lastSampleOffset > 0)
	{
		file.Seek(lastSampleOffset);
	}

	// Load instrument and song extensions.
	LoadExtendedInstrumentProperties(file, &interpretModPlugMade);
	if(interpretModPlugMade)
	{
		m_nMixLevels = mixLevels_original;
	}
	// We need to do this here, because if there no samples (so lastSampleOffset = 0), we need to look after the last pattern (sample data normally follows pattern data).
	// And we need to do this before reading the patterns because m_nChannels might be modified by LoadExtendedSongProperties. *sigh*
	LoadExtendedSongProperties(GetType(), file, &interpretModPlugMade);
	m_nTempoMode = tempo_mode_modern;

	// Reading Patterns
	Patterns.ResizeArray(std::max(MAX_PATTERNS, numPats));
	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
	{
		if(patPos[pat] == 0 || !file.Seek(patPos[pat]))
		{
			// Empty 64-row pattern
			if(Patterns.Insert(pat, 64))
			{
				AddToLog(mpt::String::Print("Allocating patterns failed starting from pattern %1", pat));
				break;
			}
			// Now (after the Insert() call), we can read the pattern name.
			CopyPatternName(Patterns[pat], patNames);
			continue;
		}

		uint16 len = file.ReadUint16LE();
		ROWINDEX numRows = file.ReadUint16LE();

		if(numRows < GetModSpecifications().patternRowsMin
			|| numRows > GetModSpecifications().patternRowsMax
			|| !file.Skip(4)
			|| Patterns.Insert(pat, numRows))
			continue;
			
		FileReader patternData = file.GetChunk(len);

		// Now (after the Insert() call), we can read the pattern name.
		CopyPatternName(Patterns[pat], patNames);

		std::vector<uint8> chnMask(GetNumChannels());
		std::vector<ModCommand> lastValue(GetNumChannels(), ModCommand::Empty());

		ModCommand *m = Patterns[pat];
		ROWINDEX row = 0;
		while(row < numRows && patternData.AreBytesLeft())
		{
			uint8 b = patternData.ReadUint8();
			if(!b)
			{
				row++;
				m += GetNumChannels();
				continue;
			}

			CHANNELINDEX ch = b & IT_bitmask_patternChanField_c; // 0x7f

			if(ch)
			{
				ch = (ch - 1); //& IT_bitmask_patternChanMask_c; // 0x3f
			}

			if(ch >= chnMask.size())
			{
				chnMask.resize(ch + 1, 0);
				lastValue.resize(ch + 1, ModCommand::Empty());
				ASSERT(chnMask.size() <= GetNumChannels());
			}

			if(b & IT_bitmask_patternChanEnabled_c)  // 0x80
			{
				chnMask[ch] = patternData.ReadUint8();
			}

			// Now we grab the data for this particular row/channel.

			if((chnMask[ch] & 0x10) && (ch < m_nChannels))
			{
				m[ch].note = lastValue[ch].note;
			}
			if((chnMask[ch] & 0x20) && (ch < m_nChannels))
			{
				m[ch].instr = lastValue[ch].instr;
			}
			if((chnMask[ch] & 0x40) && (ch < m_nChannels))
			{
				m[ch].volcmd = lastValue[ch].volcmd;
				m[ch].vol = lastValue[ch].vol;
			}
			if((chnMask[ch] & 0x80) && (ch < m_nChannels))
			{
				m[ch].command = lastValue[ch].command;
				m[ch].param = lastValue[ch].param;
			}
			if(chnMask[ch] & 1)	// Note
			{
				uint8 note = patternData.ReadUint8();
				if(ch < m_nChannels)
				{
					if(note < 0x80) note++;
					if(!(GetType() & MOD_TYPE_MPT))
					{
						if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE;
						else if(note == 0xFD) note = NOTE_NONE;
					}
					m[ch].note = note;
					lastValue[ch].note = note;
				}
			}
			if(chnMask[ch] & 2)
			{
				uint8 instr = patternData.ReadUint8();
				if(ch < m_nChannels)
				{
					m[ch].instr = instr;
					lastValue[ch].instr = instr;
				}
			}
			if(chnMask[ch] & 4)
			{
				uint8 vol = patternData.ReadUint8();
				if(ch < m_nChannels)
				{
					// 0-64: Set Volume
					if(vol <= 64) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = vol; } else
					// 128-192: Set Panning
					if(vol >= 128 && vol <= 192) { m[ch].volcmd = VOLCMD_PANNING; m[ch].vol = vol - 128; } else
					// 65-74: Fine Volume Up
					if(vol < 75) { m[ch].volcmd = VOLCMD_FINEVOLUP; m[ch].vol = vol - 65; } else
					// 75-84: Fine Volume Down
					if(vol < 85) { m[ch].volcmd = VOLCMD_FINEVOLDOWN; m[ch].vol = vol - 75; } else
					// 85-94: Volume Slide Up
					if(vol < 95) { m[ch].volcmd = VOLCMD_VOLSLIDEUP; m[ch].vol = vol - 85; } else
					// 95-104: Volume Slide Down
					if(vol < 105) { m[ch].volcmd = VOLCMD_VOLSLIDEDOWN; m[ch].vol = vol - 95; } else
					// 105-114: Pitch Slide Up
					if(vol < 115) { m[ch].volcmd = VOLCMD_PORTADOWN; m[ch].vol = vol - 105; } else
					// 115-124: Pitch Slide Down
					if(vol < 125) { m[ch].volcmd = VOLCMD_PORTAUP; m[ch].vol = vol - 115; } else
					// 193-202: Portamento To
					if(vol >= 193 && vol <= 202) { m[ch].volcmd = VOLCMD_TONEPORTAMENTO; m[ch].vol = vol - 193; } else
					// 203-212: Vibrato depth
					if(vol >= 203 && vol <= 212)
					{
						m[ch].volcmd = VOLCMD_VIBRATODEPTH; m[ch].vol = vol - 203;
						// Old versions of ModPlug saved this as vibrato speed instead, so let's fix that.
						if(m[ch].vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 17, 02, 54))
							m[ch].volcmd = VOLCMD_VIBRATOSPEED;
					} else
					// 213-222: Unused (was velocity)
					// 223-232: Offset
					if(vol >= 223 && vol <= 232) { m[ch].volcmd = VOLCMD_OFFSET; m[ch].vol = vol - 223; }
					lastValue[ch].volcmd = m[ch].volcmd;
					lastValue[ch].vol = m[ch].vol;
				}
			}
			// Reading command/param
			if(chnMask[ch] & 8)
			{
				uint8 cmd = patternData.ReadUint8();
				uint8 param = patternData.ReadUint8();
				if(ch < m_nChannels)
				{
					if(cmd)
					{
						m[ch].command = cmd;
						m[ch].param = param;
						S3MConvert(m[ch], true);
						lastValue[ch].command = m[ch].command;
						lastValue[ch].param = m[ch].param;
					}
				}
			}
		}
	}

	UpgradeModFlags();

	if(!m_dwLastSavedWithVersion && fileHeader.cwtv == 0x0888)
	{
		// There are some files with OpenMPT extensions, but the "last saved with" field contains 0.
		// Was there an OpenMPT version that wrote 0 there, or are they hacked?
		m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00);
	}

	if(m_dwLastSavedWithVersion && madeWithTracker.empty())
	{
		madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion);
		if(memcmp(fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000)
		{
			madeWithTracker += " (compatibility export)";
		} else if(MptVersion::IsTestBuild(m_dwLastSavedWithVersion))
		{
			madeWithTracker += " (test build)";
		}
	} else
	{
		switch(fileHeader.cwtv >> 12)
		{
		case 0:
			if(!madeWithTracker.empty())
			{
				// BeRoTracker has been detected above.
			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0
				&& fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0
				&& fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum
				&& fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0
				&& fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4))
			{
				madeWithTracker = "OpenSPC conversion";
			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4))
			{
				// ModPlug Tracker 1.00a5, instruments 560 bytes apart
				m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5);
				madeWithTracker = "ModPlug tracker 1.00a5";
				interpretModPlugMade = true;
			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(fileHeader.reserved, "CHBI", 4))
			{
				madeWithTracker = "ChibiTracker";
			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !(fileHeader.special & 3) && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && !strcmp(Samples[1].filename, "XXXXXXXX.YYY"))
			{
				madeWithTracker = "CheeseTracker";
			} else
			{
				if(fileHeader.cmwt > 0x0214)
				{
					madeWithTracker = "Impulse Tracker 2.15";
				} else if(fileHeader.cwtv > 0x0214)
				{
					// Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3)
					// p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change
					// anything as far as file saving is concerned.
					madeWithTracker = mpt::String::Print("Impulse Tracker 2.14p%1", fileHeader.cwtv - 0x0214);
				} else
				{
					madeWithTracker = mpt::String::Print("Impulse Tracker %1.%2", (fileHeader.cwtv & 0x0F00) >> 8, mpt::fmt::hex0<2>((fileHeader.cwtv & 0xFF)));
				}
			}
			break;
		case 1:
			madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv);
			break;
		case 6:
			madeWithTracker = "BeRoTracker";
			break;
		case 7:
			madeWithTracker = mpt::String::Print("ITMCK %1.%2.%3", (fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
			break;
		}
	}

	if(GetType() == MOD_TYPE_IT)
	{
		// Set appropriate mod flags if the file was not made with MPT.
		if(!interpretModPlugMade)
		{
			SetModFlag(MSF_MIDICC_BUGEMULATION, false);
			SetModFlag(MSF_OLDVOLSWING, false);
			SetModFlag(MSF_COMPATIBLE_PLAY, true);
		}
	} else
	{
		//START - mpt specific:
		//Using member cwtv on pifh as the version number.
		const uint16 version = fileHeader.cwtv;
		if(version > 0x889 && file.Seek(mptStartPos))
		{
			std::istringstream iStrm(std::string(file.GetRawData(), file.BytesLeft()));

			if(version >= 0x88D)
			{
				srlztn::SsbRead ssb(iStrm);
				ssb.BeginRead("mptm", MptVersion::num);
				ssb.ReadItem(GetTuneSpecificTunings(), "0", 1, &ReadTuningCollection);
				ssb.ReadItem(*this, "1", 1, &ReadTuningMap);
				ssb.ReadItem(Order, "2", 1, &ReadModSequenceOld);
				ssb.ReadItem(Patterns, FileIdPatterns, strlen(FileIdPatterns), &ReadModPatterns);
				ssb.ReadItem(Order, FileIdSequences, strlen(FileIdSequences), &ReadModSequences);

				if(ssb.m_Status & srlztn::SNT_FAILURE)
				{
					AddToLog("Unknown error occured while deserializing file.");
				}
			} else //Loading for older files.
			{
				if(GetTuneSpecificTunings().Deserialize(iStrm))
				{
					AddToLog("Error occured - loading failed while trying to load tune specific tunings.");
				} else
				{
					ReadTuningMap(iStrm, *this);
				}
			}
		} //version condition(MPT)
	}

	return true;
}