Example #1
0
BOOL CSoundFile::ReadXM(const BYTE *lpStream, DWORD dwMemLength)
//--------------------------------------------------------------
{
	XMSAMPLEHEADER xmsh;
	XMSAMPLESTRUCT xmss;
	DWORD dwMemPos, dwHdrSize;
	WORD norders=0, restartpos=0, channels=0, patterns=0, instruments=0;
	WORD xmflags=0, deftempo=125, defspeed=6;
	BOOL InstUsed[256];
	BYTE channels_used[MAX_CHANNELS];
	BYTE pattern_map[256];
	BOOL samples_used[MAX_SAMPLES];
	UINT unused_samples;
	tagXMFILEHEADER xmhead;

	m_nChannels = 0;
	if ((!lpStream) || (dwMemLength < 0x200)) return FALSE;
	if (strncmp((LPCSTR)lpStream, "Extended Module:", 16)) return FALSE;

	memcpy(m_szNames[0], lpStream+17, 20);
	xmhead = *(tagXMFILEHEADER *)(lpStream+60);
	dwHdrSize = bswapLE32(xmhead.size);
	norders = bswapLE16(xmhead.norder);
	if ((!norders) || (norders > MAX_ORDERS)) return FALSE;
	restartpos = bswapLE16(xmhead.restartpos);
	channels = bswapLE16(xmhead.channels);
	if ((!channels) || (channels > 64)) return FALSE;
	m_nType = MOD_TYPE_XM;
	m_nMinPeriod = 27;
	m_nMaxPeriod = 54784;
	m_nChannels = channels;
	if (restartpos < norders) m_nRestartPos = restartpos;
	patterns = bswapLE16(xmhead.patterns);
	if (patterns > 256) patterns = 256;
	instruments = bswapLE16(xmhead.instruments);
	if (instruments >= MAX_INSTRUMENTS) instruments = MAX_INSTRUMENTS-1;
	m_nInstruments = instruments;
	m_nSamples = 0;
	xmflags = bswapLE16(xmhead.flags);
	if (xmflags & 1) m_dwSongFlags |= SONG_LINEARSLIDES;
	if (xmflags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE;
	defspeed = bswapLE16(xmhead.speed);
	deftempo = bswapLE16(xmhead.tempo);
	if ((deftempo >= 32) && (deftempo < 256)) m_nDefaultTempo = deftempo;
	if ((defspeed > 0) && (defspeed < 40)) m_nDefaultSpeed = defspeed;
	memcpy(Order, lpStream+80, norders);
	memset(InstUsed, 0, sizeof(InstUsed));
	if (patterns > MAX_PATTERNS)
	{
		UINT i, j;
		for (i=0; i<norders; i++)
		{
			if (Order[i] < patterns) InstUsed[Order[i]] = TRUE;
		}
		j = 0;
		for (i=0; i<256; i++)
		{
			if (InstUsed[i]) pattern_map[i] = j++;
		}
		for (i=0; i<256; i++)
		{
			if (!InstUsed[i])
			{
				pattern_map[i] = (j < MAX_PATTERNS) ? j : 0xFE;
				j++;
			}
		}
		for (i=0; i<norders; i++)
		{
			Order[i] = pattern_map[Order[i]];
		}
	} else
	{
		for (UINT i=0; i<256; i++) pattern_map[i] = i;
	}
	memset(InstUsed, 0, sizeof(InstUsed));
	dwMemPos = dwHdrSize + 60;
	if (dwMemPos + 8 >= dwMemLength) return TRUE;
	// Reading patterns
	memset(channels_used, 0, sizeof(channels_used));
	for (UINT ipat=0; ipat<patterns; ipat++)
	{
		UINT ipatmap = pattern_map[ipat];
		DWORD dwSize = 0;
		WORD rows=64, packsize=0;
		dwSize = bswapLE32(loadDWORD(lpStream+dwMemPos));
		while ((dwMemPos + dwSize >= dwMemLength) || (dwSize & 0xFFFFFF00))
		{
			if (dwMemPos + 4 >= dwMemLength) break;
			dwMemPos++;
			dwSize = bswapLE32(loadDWORD(lpStream+dwMemPos));
		}
		rows = bswapLE16(loadWORD(lpStream+dwMemPos+5));
		if ((!rows) || (rows > 256)) rows = 64;
		packsize = bswapLE16(loadWORD(lpStream+dwMemPos+7));
		if (dwMemPos + dwSize + 4 > dwMemLength) return TRUE;
		dwMemPos += dwSize;
		if (dwMemPos + packsize + 4 > dwMemLength) return TRUE;
		MODCOMMAND *p;
		if (ipatmap < MAX_PATTERNS)
		{
			PatternSize[ipatmap] = rows;
			if ((Patterns[ipatmap] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE;
			if (!packsize) continue;
			p = Patterns[ipatmap];
		} else p = NULL;
		const BYTE *src = lpStream+dwMemPos;
		UINT j=0;
		for (UINT row=0; row<rows; row++)
		{
			for (UINT chn=0; chn<m_nChannels; chn++)
			{
				if ((p) && (j < packsize))
				{
					BYTE b = src[j++];
					UINT vol = 0;
					if (b & 0x80)
					{
						if (b & 1) p->note = src[j++];
						if (b & 2) p->instr = src[j++];
						if (b & 4) vol = src[j++];
						if (b & 8) p->command = src[j++];
						if (b & 16) p->param = src[j++];
					} else
					{
						p->note = b;
						p->instr = src[j++];
						vol = src[j++];
						p->command = src[j++];
						p->param = src[j++];
					}
					if (p->note == 97) p->note = 0xFF; else
					if ((p->note) && (p->note < 97)) p->note += 12;
					if (p->note) channels_used[chn] = 1;
					if (p->command | p->param) ConvertModCommand(p);
					if (p->instr == 0xff) p->instr = 0;
					if (p->instr) InstUsed[p->instr] = TRUE;
					if ((vol >= 0x10) && (vol <= 0x50))
					{
						p->volcmd = VOLCMD_VOLUME;
						p->vol = vol - 0x10;
					} else
					if (vol >= 0x60)
					{
						UINT v = vol & 0xF0;
						vol &= 0x0F;
						p->vol = vol;
						switch(v)
						{
						// 60-6F: Volume Slide Down
						case 0x60:	p->volcmd = VOLCMD_VOLSLIDEDOWN; break;
						// 70-7F: Volume Slide Up:
						case 0x70:	p->volcmd = VOLCMD_VOLSLIDEUP; break;
						// 80-8F: Fine Volume Slide Down
						case 0x80:	p->volcmd = VOLCMD_FINEVOLDOWN; break;
						// 90-9F: Fine Volume Slide Up
						case 0x90:	p->volcmd = VOLCMD_FINEVOLUP; break;
						// A0-AF: Set Vibrato Speed
						case 0xA0:	p->volcmd = VOLCMD_VIBRATOSPEED; break;
						// B0-BF: Vibrato
						case 0xB0:	p->volcmd = VOLCMD_VIBRATO; break;
						// C0-CF: Set Panning
						case 0xC0:	p->volcmd = VOLCMD_PANNING; p->vol = (vol << 2) + 2; break;
						// D0-DF: Panning Slide Left
						case 0xD0:	p->volcmd = VOLCMD_PANSLIDELEFT; break;
						// E0-EF: Panning Slide Right
						case 0xE0:	p->volcmd = VOLCMD_PANSLIDERIGHT; break;
						// F0-FF: Tone Portamento
						case 0xF0:	p->volcmd = VOLCMD_TONEPORTAMENTO; break;
						}
					}
					p++;
				} else
				if (j < packsize)
				{
					BYTE b = src[j++];
					if (b & 0x80)
					{
						if (b & 1) j++;
						if (b & 2) j++;
						if (b & 4) j++;
						if (b & 8) j++;
						if (b & 16) j++;
					} else j += 4;
				} else break;
			}
		}
		dwMemPos += packsize;
	}
	// Wrong offset check
	while (dwMemPos + 4 < dwMemLength)
	{
		DWORD d = bswapLE32(loadDWORD(lpStream+dwMemPos));
		if (d < 0x300) break;
		dwMemPos++;
	}
	memset(samples_used, 0, sizeof(samples_used));
	unused_samples = 0;
	// Reading instruments
	for (UINT iIns=1; iIns<=instruments; iIns++)
	{
		XMINSTRUMENTHEADER *pih;
		BYTE flags[32];
		DWORD samplesize[32];
		UINT samplemap[32];
		WORD nsamples;

		if (dwMemPos + sizeof(XMINSTRUMENTHEADER) >= dwMemLength) return TRUE;
		pih = (XMINSTRUMENTHEADER *)(lpStream+dwMemPos);
		if (dwMemPos + bswapLE32(pih->size) > dwMemLength) return TRUE;
		if ((Headers[iIns] = new INSTRUMENTHEADER) == NULL) continue;
		memset(Headers[iIns], 0, sizeof(INSTRUMENTHEADER));
		memcpy(Headers[iIns]->name, pih->name, 22);
		if ((nsamples = pih->samples) > 0)
		{
			if (dwMemPos + sizeof(XMSAMPLEHEADER) > dwMemLength) return TRUE;
			memcpy(&xmsh, lpStream+dwMemPos+sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER));
			xmsh.shsize = bswapLE32(xmsh.shsize);
			for (int i = 0; i < 24; ++i) {
			  xmsh.venv[i] = bswapLE16(xmsh.venv[i]);
			  xmsh.penv[i] = bswapLE16(xmsh.penv[i]);
			}
			xmsh.volfade = bswapLE16(xmsh.volfade);
			xmsh.res = bswapLE16(xmsh.res);
			dwMemPos += bswapLE32(pih->size);
		} else
		{
			if (bswapLE32(pih->size)) dwMemPos += bswapLE32(pih->size);
			else dwMemPos += sizeof(XMINSTRUMENTHEADER);
			continue;
		}
		memset(samplemap, 0, sizeof(samplemap));
		if (nsamples > 32) return TRUE;
		UINT newsamples = m_nSamples;
		for (UINT nmap=0; nmap<nsamples; nmap++)
		{
			UINT n = m_nSamples+nmap+1;
			if (n >= MAX_SAMPLES)
			{
				n = m_nSamples;
				while (n > 0)
				{
					if (!Ins[n].pSample)
					{
						for (UINT xmapchk=0; xmapchk < nmap; xmapchk++)
						{
							if (samplemap[xmapchk] == n) goto alreadymapped;
						}
						for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs])
						{
							INSTRUMENTHEADER *pks = Headers[clrs];
							for (UINT ks=0; ks<128; ks++)
							{
								if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
							}
						}
						break;
					}
				alreadymapped:
					n--;
				}
#ifndef MODPLUG_FASTSOUNDLIB
				// Damn! more than 200 samples: look for duplicates
				if (!n)
				{
					if (!unused_samples)
					{
						unused_samples = DetectUnusedSamples(samples_used);
						if (!unused_samples) unused_samples = 0xFFFF;
					}
					if ((unused_samples) && (unused_samples != 0xFFFF))
					{
						for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext])
						{
							unused_samples--;
							samples_used[iext] = TRUE;
							DestroySample(iext);
							n = iext;
							for (UINT mapchk=0; mapchk<nmap; mapchk++)
							{
								if (samplemap[mapchk] == n) samplemap[mapchk] = 0;
							}
							for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs])
							{
								INSTRUMENTHEADER *pks = Headers[clrs];
								for (UINT ks=0; ks<128; ks++)
								{
									if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
								}
							}
							memset(&Ins[n], 0, sizeof(Ins[0]));
							break;
						}
					}
				}
#endif // MODPLUG_FASTSOUNDLIB
			}
			if (newsamples < n) newsamples = n;
			samplemap[nmap] = n;
		}
		m_nSamples = newsamples;
		// Reading Volume Envelope
		INSTRUMENTHEADER *penv = Headers[iIns];
		penv->nMidiProgram = pih->type;
		penv->nFadeOut = xmsh.volfade;
		penv->nPan = 128;
		penv->nPPC = 5*12;
		if (xmsh.vtype & 1) penv->dwFlags |= ENV_VOLUME;
		if (xmsh.vtype & 2) penv->dwFlags |= ENV_VOLSUSTAIN;
		if (xmsh.vtype & 4) penv->dwFlags |= ENV_VOLLOOP;
		if (xmsh.ptype & 1) penv->dwFlags |= ENV_PANNING;
		if (xmsh.ptype & 2) penv->dwFlags |= ENV_PANSUSTAIN;
		if (xmsh.ptype & 4) penv->dwFlags |= ENV_PANLOOP;
		if (xmsh.vnum > 12) xmsh.vnum = 12;
		if (xmsh.pnum > 12) xmsh.pnum = 12;
		penv->nVolEnv = xmsh.vnum;
		if (!xmsh.vnum) penv->dwFlags &= ~ENV_VOLUME;
		if (!xmsh.pnum) penv->dwFlags &= ~ENV_PANNING;
		penv->nPanEnv = xmsh.pnum;
		penv->nVolSustainBegin = penv->nVolSustainEnd = xmsh.vsustain;
		if (xmsh.vsustain >= 12) penv->dwFlags &= ~ENV_VOLSUSTAIN;
		penv->nVolLoopStart = xmsh.vloops;
		penv->nVolLoopEnd = xmsh.vloope;
		if (penv->nVolLoopEnd >= 12) penv->nVolLoopEnd = 0;
		if (penv->nVolLoopStart >= penv->nVolLoopEnd) penv->dwFlags &= ~ENV_VOLLOOP;
		penv->nPanSustainBegin = penv->nPanSustainEnd = xmsh.psustain;
		if (xmsh.psustain >= 12) penv->dwFlags &= ~ENV_PANSUSTAIN;
		penv->nPanLoopStart = xmsh.ploops;
		penv->nPanLoopEnd = xmsh.ploope;
		if (penv->nPanLoopEnd >= 12) penv->nPanLoopEnd = 0;
		if (penv->nPanLoopStart >= penv->nPanLoopEnd) penv->dwFlags &= ~ENV_PANLOOP;
		penv->nGlobalVol = 64;
		for (UINT ienv=0; ienv<12; ienv++)
		{
			penv->VolPoints[ienv] = (WORD)xmsh.venv[ienv*2];
			penv->VolEnv[ienv] = (BYTE)xmsh.venv[ienv*2+1];
			penv->PanPoints[ienv] = (WORD)xmsh.penv[ienv*2];
			penv->PanEnv[ienv] = (BYTE)xmsh.penv[ienv*2+1];
			if (ienv)
			{
				if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1])
				{
					penv->VolPoints[ienv] &= 0xFF;
					penv->VolPoints[ienv] += penv->VolPoints[ienv-1] & 0xFF00;
					if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) penv->VolPoints[ienv] += 0x100;
				}
				if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1])
				{
					penv->PanPoints[ienv] &= 0xFF;
					penv->PanPoints[ienv] += penv->PanPoints[ienv-1] & 0xFF00;
					if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) penv->PanPoints[ienv] += 0x100;
				}
			}
		}
		for (UINT j=0; j<96; j++)
		{
			penv->NoteMap[j+12] = j+1+12;
			if (xmsh.snum[j] < nsamples)
				penv->Keyboard[j+12] = samplemap[xmsh.snum[j]];
		}
		// Reading samples
		for (UINT ins=0; ins<nsamples; ins++)
		{
			if ((dwMemPos + sizeof(xmss) > dwMemLength)
			 || (dwMemPos + xmsh.shsize > dwMemLength)) return TRUE;
			memcpy(&xmss, lpStream+dwMemPos, sizeof(xmss));
			xmss.samplen = bswapLE32(xmss.samplen);
			xmss.loopstart = bswapLE32(xmss.loopstart);
			xmss.looplen = bswapLE32(xmss.looplen);
			dwMemPos += xmsh.shsize;
			flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D;
			if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D;
			samplesize[ins] = xmss.samplen;
			if (!samplemap[ins]) continue;
			if (xmss.type & 0x10)
			{
				xmss.looplen >>= 1;
				xmss.loopstart >>= 1;
				xmss.samplen >>= 1;
			}
			if (xmss.type & 0x20)
			{
				xmss.looplen >>= 1;
				xmss.loopstart >>= 1;
				xmss.samplen >>= 1;
			}
			if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH;
			if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3;
			xmss.looplen += xmss.loopstart;
			if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen;
			if (!xmss.looplen) xmss.type &= ~3;
			UINT imapsmp = samplemap[ins];
			memcpy(m_szNames[imapsmp], xmss.name, 22);
			m_szNames[imapsmp][22] = 0;
			MODINSTRUMENT *pins = &Ins[imapsmp];
			pins->nLength = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen;
			pins->nLoopStart = xmss.loopstart;
			pins->nLoopEnd = xmss.looplen;
			if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength;
			if (pins->nLoopStart >= pins->nLoopEnd)
			{
				pins->nLoopStart = pins->nLoopEnd = 0;
			}
			if (xmss.type & 3) pins->uFlags |= CHN_LOOP;
			if (xmss.type & 2) pins->uFlags |= CHN_PINGPONGLOOP;
			pins->nVolume = xmss.vol << 2;
			if (pins->nVolume > 256) pins->nVolume = 256;
			pins->nGlobalVol = 64;
			if ((xmss.res == 0xAD) && (!(xmss.type & 0x30)))
			{
				flags[ins] = RS_ADPCM4;
				samplesize[ins] = (samplesize[ins]+1)/2 + 16;
			}
			pins->nFineTune = xmss.finetune;
			pins->RelativeTone = (int)xmss.relnote;
			pins->nPan = xmss.pan;
			pins->uFlags |= CHN_PANNING;
			pins->nVibType = xmsh.vibtype;
			pins->nVibSweep = xmsh.vibsweep;
			pins->nVibDepth = xmsh.vibdepth;
			pins->nVibRate = xmsh.vibrate;
			memcpy(pins->name, xmss.name, 22);
			pins->name[21] = 0;
		}
Example #2
0
bool module_renderer::ReadXM(const uint8_t *lpStream, const uint32_t dwMemLength)
//--------------------------------------------------------------------
{
    XMFILEHEADER xmheader;
    XMSAMPLEHEADER xmsh;
    XMSAMPLESTRUCT xmss;
    uint32_t dwMemPos;

    bool bMadeWithModPlug = false, bProbablyMadeWithModPlug = false, bProbablyMPT109 = false, bIsFT2 = false;

    m_nChannels = 0;
    if ((!lpStream) || (dwMemLength < 0xAA)) return false; // the smallest XM I know is 174 Bytes
    if (_strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return false;

    // look for null-terminated song name - that's most likely a tune made with modplug
    for(int i = 0; i < 20; i++)
        if(lpStream[17 + i] == 0) bProbablyMadeWithModPlug = true;
    assign_without_padding(this->song_name, reinterpret_cast<const char *>(lpStream + 17), 20);

    // load and convert header
    memcpy(&xmheader, lpStream + 58, sizeof(XMFILEHEADER));
    xmheader.size = LittleEndian(xmheader.size);
    xmheader.xmversion = LittleEndianW(xmheader.xmversion);
    xmheader.orders = LittleEndianW(xmheader.orders);
    xmheader.restartpos = LittleEndianW(xmheader.restartpos);
    xmheader.channels = LittleEndianW(xmheader.channels);
    xmheader.patterns = LittleEndianW(xmheader.patterns);
    xmheader.instruments = LittleEndianW(xmheader.instruments);
    xmheader.flags = LittleEndianW(xmheader.flags);
    xmheader.speed = LittleEndianW(xmheader.speed);
    xmheader.tempo = LittleEndianW(xmheader.tempo);

    m_nType = MOD_TYPE_XM;
    m_nMinPeriod = 27;
    m_nMaxPeriod = 54784;

    if (xmheader.orders > MAX_ORDERS) return false;
    if ((!xmheader.channels) || (xmheader.channels > MAX_BASECHANNELS)) return false;
    if (xmheader.channels > 32) bMadeWithModPlug = true;
    m_nRestartPos = xmheader.restartpos;
    m_nChannels = xmheader.channels;
    m_nInstruments = bad_min(xmheader.instruments, MAX_INSTRUMENTS - 1);
    m_nSamples = 0;
    m_nDefaultSpeed = CLAMP(xmheader.speed, 1, 31);
    m_nDefaultTempo = CLAMP(xmheader.tempo, 32, 512);

    if(xmheader.flags & 1) m_dwSongFlags |= SONG_LINEARSLIDES;
    if(xmheader.flags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE;

    Order.ReadAsByte(lpStream + 80, xmheader.orders, dwMemLength - 80);

    dwMemPos = xmheader.size + 60;

    // set this here already because XMs compressed with BoobieSqueezer will exit the function early
    SetModFlag(MSF_COMPATIBLE_PLAY, true);

    if(xmheader.xmversion >= 0x0104)
    {
        if (dwMemPos + 8 >= dwMemLength) return true;
        dwMemPos = ReadXMPatterns(lpStream, dwMemLength, dwMemPos, &xmheader, this);
        if(dwMemPos == 0) return true;
    }

    vector<bool> samples_used; // for removing unused samples
    modplug::tracker::sampleindex_t unused_samples = 0; // dito

    // Reading instruments
    for (modplug::tracker::instrumentindex_t iIns = 1; iIns <= m_nInstruments; iIns++)
    {
        XMINSTRUMENTHEADER pih;
        uint8_t flags[32];
        uint32_t samplesize[32];
        UINT samplemap[32];
        uint16_t nsamples;

        if (dwMemPos + sizeof(uint32_t) >= dwMemLength) return true;
        uint32_t ihsize = LittleEndian(*((uint32_t *)(lpStream + dwMemPos)));
        if (dwMemPos + ihsize > dwMemLength) return true;

        MemsetZero(pih);
        memcpy(&pih, lpStream + dwMemPos, bad_min(sizeof(pih), ihsize));

        if ((Instruments[iIns] = new modinstrument_t) == nullptr) continue;
        memcpy(Instruments[iIns], &m_defaultInstrument, sizeof(modinstrument_t));
        Instruments[iIns]->nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL;
        Instruments[iIns]->nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE;

        memcpy(Instruments[iIns]->name, pih.name, 22);
        SpaceToNullStringFixed<22>(Instruments[iIns]->name);

        memset(&xmsh, 0, sizeof(XMSAMPLEHEADER));

        if ((nsamples = pih.samples) > 0)
        {
            /* we have samples, so let's read the rest of this instrument
               the header that is being read here is not the sample header, though,
               it's rather the instrument settings. */

            if (dwMemPos + ihsize >= dwMemLength)
                return true;

            memcpy(&xmsh,
                lpStream + dwMemPos + sizeof(XMINSTRUMENTHEADER),
                bad_min(ihsize - sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER)));

            xmsh.shsize = LittleEndian(xmsh.shsize);
            if(xmsh.shsize == 0 && bProbablyMadeWithModPlug) bMadeWithModPlug = true;

            for (int i = 0; i < 24; ++i) {
                xmsh.venv[i] = LittleEndianW(xmsh.venv[i]);
                xmsh.penv[i] = LittleEndianW(xmsh.penv[i]);
            }
            xmsh.volfade = LittleEndianW(xmsh.volfade);
            xmsh.midiprogram = LittleEndianW(xmsh.midiprogram);
            xmsh.pitchwheelrange = LittleEndianW(xmsh.pitchwheelrange);

            if(xmsh.midichannel != 0 || xmsh.midienabled != 0 || xmsh.midiprogram != 0 || xmsh.mutecomputer != 0 || xmsh.pitchwheelrange != 0)
                bIsFT2 = true; // definitely not MPT. (or any other tracker)

        }

        if (LittleEndian(pih.size))
            dwMemPos += LittleEndian(pih.size);
        else
            dwMemPos += sizeof(XMINSTRUMENTHEADER);

        memset(samplemap, 0, sizeof(samplemap));
        if (nsamples > 32) return true;
        UINT newsamples = m_nSamples;

        for (UINT nmap = 0; nmap < nsamples; nmap++)
        {
            UINT n = m_nSamples + nmap + 1;
            if (n >= MAX_SAMPLES)
            {
                n = m_nSamples;
                while (n > 0)
                {
                    if (!Samples[n].sample_data)
                    {
                        for (UINT xmapchk=0; xmapchk < nmap; xmapchk++)
                        {
                            if (samplemap[xmapchk] == n) goto alreadymapped;
                        }
                        for (UINT clrs=1; clrs<iIns; clrs++) if (Instruments[clrs])
                        {
                            modinstrument_t *pks = Instruments[clrs];
                            for (UINT ks=0; ks<128; ks++)
                            {
                                if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
                            }
                        }
                        break;
                    }
                alreadymapped:
                    n--;
                }
#ifndef FASTSOUNDLIB
                // Damn! Too many samples: look for duplicates
                if (!n)
                {
                    if (!unused_samples)
                    {
                        unused_samples = DetectUnusedSamples(samples_used);
                        if (!unused_samples) unused_samples = modplug::tracker::SampleIndexInvalid;
                    }
                    if ((unused_samples) && (unused_samples != modplug::tracker::SampleIndexInvalid))
                    {
                        for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext])
                        {
                            unused_samples--;
                            samples_used[iext] = true;
                            DestroySample(iext);
                            n = iext;
                            for (UINT mapchk=0; mapchk<nmap; mapchk++)
                            {
                                if (samplemap[mapchk] == n) samplemap[mapchk] = 0;
                            }
                            for (UINT clrs=1; clrs<iIns; clrs++) if (Instruments[clrs])
                            {
                                modinstrument_t *pks = Instruments[clrs];
                                for (UINT ks=0; ks<128; ks++)
                                {
                                    if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
                                }
                            }
                            MemsetZero(Samples[n]);
                            break;
                        }
                    }
                }
#endif // FASTSOUNDLIB
            }
            if (newsamples < n) newsamples = n;
            samplemap[nmap] = n;
        }
        m_nSamples = newsamples;
        // Reading Volume Envelope
        modinstrument_t *pIns = Instruments[iIns];
        pIns->midi_program = pih.type;
        pIns->fadeout = xmsh.volfade;
        pIns->default_pan = 128;
        pIns->pitch_pan_center = 5*12;
        SetDefaultInstrumentValues(pIns);
        pIns->nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL;
        pIns->nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE;
        if (xmsh.vtype & 1) pIns->volume_envelope.flags |= ENV_ENABLED;
        if (xmsh.vtype & 2) pIns->volume_envelope.flags |= ENV_SUSTAIN;
        if (xmsh.vtype & 4) pIns->volume_envelope.flags |= ENV_LOOP;
        if (xmsh.ptype & 1) pIns->panning_envelope.flags |= ENV_ENABLED;
        if (xmsh.ptype & 2) pIns->panning_envelope.flags |= ENV_SUSTAIN;
        if (xmsh.ptype & 4) pIns->panning_envelope.flags |= ENV_LOOP;
        if (xmsh.vnum > 12) xmsh.vnum = 12;
        if (xmsh.pnum > 12) xmsh.pnum = 12;
        pIns->volume_envelope.num_nodes = xmsh.vnum;
        if (!xmsh.vnum) pIns->volume_envelope.flags &= ~ENV_ENABLED;
        if (!xmsh.pnum) pIns->panning_envelope.flags &= ~ENV_ENABLED;
        pIns->panning_envelope.num_nodes = xmsh.pnum;
        pIns->volume_envelope.sustain_start = pIns->volume_envelope.sustain_end = xmsh.vsustain;
        if (xmsh.vsustain >= 12) pIns->volume_envelope.flags &= ~ENV_SUSTAIN;
        pIns->volume_envelope.loop_start = xmsh.vloops;
        pIns->volume_envelope.loop_end = xmsh.vloope;
        if (pIns->volume_envelope.loop_end >= 12) pIns->volume_envelope.loop_end = 0;
        if (pIns->volume_envelope.loop_start >= pIns->volume_envelope.loop_end) pIns->volume_envelope.flags &= ~ENV_LOOP;
        pIns->panning_envelope.sustain_start = pIns->panning_envelope.sustain_end = xmsh.psustain;
        if (xmsh.psustain >= 12) pIns->panning_envelope.flags &= ~ENV_SUSTAIN;
        pIns->panning_envelope.loop_start = xmsh.ploops;
        pIns->panning_envelope.loop_end = xmsh.ploope;
        if (pIns->panning_envelope.loop_end >= 12) pIns->panning_envelope.loop_end = 0;
        if (pIns->panning_envelope.loop_start >= pIns->panning_envelope.loop_end) pIns->panning_envelope.flags &= ~ENV_LOOP;
        pIns->global_volume = 64;
        for (UINT ienv=0; ienv<12; ienv++)
        {
            pIns->volume_envelope.Ticks[ienv] = (uint16_t)xmsh.venv[ienv*2];
            pIns->volume_envelope.Values[ienv] = (uint8_t)xmsh.venv[ienv*2+1];
            pIns->panning_envelope.Ticks[ienv] = (uint16_t)xmsh.penv[ienv*2];
            pIns->panning_envelope.Values[ienv] = (uint8_t)xmsh.penv[ienv*2+1];
            if (ienv)
            {
                if (pIns->volume_envelope.Ticks[ienv] < pIns->volume_envelope.Ticks[ienv-1])
                {
                    pIns->volume_envelope.Ticks[ienv] &= 0xFF;
                    pIns->volume_envelope.Ticks[ienv] += pIns->volume_envelope.Ticks[ienv-1] & 0xFF00;
                    if (pIns->volume_envelope.Ticks[ienv] < pIns->volume_envelope.Ticks[ienv-1]) pIns->volume_envelope.Ticks[ienv] += 0x100;
                }
                if (pIns->panning_envelope.Ticks[ienv] < pIns->panning_envelope.Ticks[ienv-1])
                {
                    pIns->panning_envelope.Ticks[ienv] &= 0xFF;
                    pIns->panning_envelope.Ticks[ienv] += pIns->panning_envelope.Ticks[ienv-1] & 0xFF00;
                    if (pIns->panning_envelope.Ticks[ienv] < pIns->panning_envelope.Ticks[ienv-1]) pIns->panning_envelope.Ticks[ienv] += 0x100;
                }
            }
        }
        for (UINT j=0; j<96; j++)
        {
            pIns->NoteMap[j+12] = j+1+12;
            if (xmsh.snum[j] < nsamples)
                pIns->Keyboard[j+12] = samplemap[xmsh.snum[j]];
        }
        // Reading samples
        for (UINT ins=0; ins<nsamples; ins++)
        {
            if ((dwMemPos + sizeof(xmss) > dwMemLength)
             || (dwMemPos + xmsh.shsize > dwMemLength)) return true;
            memcpy(&xmss, lpStream + dwMemPos, sizeof(xmss));
            xmss.samplen = LittleEndian(xmss.samplen);
            xmss.loopstart = LittleEndian(xmss.loopstart);
            xmss.looplen = LittleEndian(xmss.looplen);
            dwMemPos += sizeof(XMSAMPLESTRUCT);    // was: dwMemPos += xmsh.shsize; (this fixes IFULOVE.XM)
            flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D;
            if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D;
            samplesize[ins] = xmss.samplen;
            if (!samplemap[ins]) continue;
            if (xmss.type & 0x10)
            {
                xmss.looplen >>= 1;
                xmss.loopstart >>= 1;
                xmss.samplen >>= 1;
            }
            if (xmss.type & 0x20)
            {
                xmss.looplen >>= 1;
                xmss.loopstart >>= 1;
                xmss.samplen >>= 1;
            }
            if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH;
            if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3;
            xmss.looplen += xmss.loopstart;
            if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen;
            if (!xmss.looplen) xmss.type &= ~3;
            UINT imapsmp = samplemap[ins];
            memcpy(m_szNames[imapsmp], xmss.name, 22);
            SpaceToNullStringFixed<22>(m_szNames[imapsmp]);
            modsample_t *pSmp = &Samples[imapsmp];
            pSmp->length = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen;
            pSmp->loop_start = xmss.loopstart;
            pSmp->loop_end = xmss.looplen;
            if (pSmp->loop_end > pSmp->length) pSmp->loop_end = pSmp->length;
            if (pSmp->loop_start >= pSmp->loop_end)
            {
                pSmp->loop_start = pSmp->loop_end = 0;
            }
            if (xmss.type & 3) pSmp->flags |= CHN_LOOP;
            if (xmss.type & 2) pSmp->flags |= CHN_PINGPONGLOOP;
            pSmp->default_volume = xmss.vol << 2;
            if (pSmp->default_volume > 256) pSmp->default_volume = 256;
            pSmp->global_volume = 64;
            if ((xmss.res == 0xAD) && (!(xmss.type & 0x30)))
            {
                flags[ins] = RS_ADPCM4;
                samplesize[ins] = (samplesize[ins]+1)/2 + 16;
            }
            pSmp->nFineTune = xmss.finetune;
            pSmp->RelativeTone = (int)xmss.relnote;
            pSmp->default_pan = xmss.pan;
            pSmp->flags |= CHN_PANNING;
            pSmp->vibrato_type = xmsh.vibtype;
            pSmp->vibrato_sweep = xmsh.vibsweep;
            pSmp->vibrato_depth = xmsh.vibdepth;
            pSmp->vibrato_rate = xmsh.vibrate;
            memcpy(pSmp->legacy_filename, xmss.name, 22);
            SpaceToNullStringFixed<21>(pSmp->legacy_filename);

            if ((xmss.type & 3) == 3)    // MPT 1.09 and maybe newer / older versions set both flags for bidi loops
                bProbablyMPT109 = true;
        }