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; }
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; }