Mercurial > audlegacy-plugins
view src/modplug/load_xm.cxx @ 2023:27239c2e3b47
iabervon: We appreciate the plugin you wrote for the VFS system, but,
it's now deprecated. We look forward to consider other plugins of yours
in the future. (curl plugin dropped from distribution, in favour of neon).
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Thu, 11 Oct 2007 15:40:36 -0500 |
parents | 6b5a52635b3b |
children | 032053ca08ab 3673c7ec4ea2 |
line wrap: on
line source
/* * This source code is public domain. * * Authors: Olivier Lapicque <olivierl@jps.net>, * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) */ #include "stdafx.h" #include "sndfile.h" //////////////////////////////////////////////////////// // FastTracker II XM file support #ifdef MSC_VER #pragma warning(disable:4244) #endif #pragma pack(1) typedef struct tagXMFILEHEADER { DWORD size; WORD norder; WORD restartpos; WORD channels; WORD patterns; WORD instruments; WORD flags; WORD speed; WORD tempo; BYTE order[256]; } XMFILEHEADER; typedef struct tagXMINSTRUMENTHEADER { DWORD size; CHAR name[22]; BYTE type; BYTE samples; BYTE samplesh; } XMINSTRUMENTHEADER; typedef struct tagXMSAMPLEHEADER { DWORD shsize; BYTE snum[96]; WORD venv[24]; WORD penv[24]; BYTE vnum, pnum; BYTE vsustain, vloops, vloope, psustain, ploops, ploope; BYTE vtype, ptype; BYTE vibtype, vibsweep, vibdepth, vibrate; WORD volfade; WORD res; BYTE reserved1[20]; } XMSAMPLEHEADER; typedef struct tagXMSAMPLESTRUCT { DWORD samplen; DWORD loopstart; DWORD looplen; BYTE vol; signed char finetune; BYTE type; BYTE pan; signed char relnote; BYTE res; char name[22]; } XMSAMPLESTRUCT; #pragma pack() 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; m_nChannels = 0; if ((!lpStream) || (dwMemLength < 0x200)) return FALSE; if (strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return FALSE; memcpy(m_szNames[0], lpStream+17, 20); dwHdrSize = bswapLE32(*((DWORD *)(lpStream+60))); norders = bswapLE16(*((WORD *)(lpStream+64))); if ((!norders) || (norders > MAX_ORDERS)) return FALSE; restartpos = bswapLE16(*((WORD *)(lpStream+66))); channels = bswapLE16(*((WORD *)(lpStream+68))); 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(*((WORD *)(lpStream+70))); if (patterns > 256) patterns = 256; instruments = bswapLE16(*((WORD *)(lpStream+72))); if (instruments >= MAX_INSTRUMENTS) instruments = MAX_INSTRUMENTS-1; m_nInstruments = instruments; m_nSamples = 0; memcpy(&xmflags, lpStream+74, 2); xmflags = bswapLE16(xmflags); if (xmflags & 1) m_dwSongFlags |= SONG_LINEARSLIDES; if (xmflags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; defspeed = bswapLE16(*((WORD *)(lpStream+76))); deftempo = bswapLE16(*((WORD *)(lpStream+78))); 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(*((DWORD *)(lpStream+dwMemPos))); while ((dwMemPos + dwSize >= dwMemLength) || (dwSize & 0xFFFFFF00)) { if (dwMemPos + 4 >= dwMemLength) break; dwMemPos++; dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); } rows = bswapLE16(*((WORD *)(lpStream+dwMemPos+5))); if ((!rows) || (rows > 256)) rows = 64; packsize = bswapLE16(*((WORD *)(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(*((DWORD *)(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; } #if 0 if ((xmsh.reserved2 > nsamples) && (xmsh.reserved2 <= 16)) { dwMemPos += (((UINT)xmsh.reserved2) - nsamples) * xmsh.shsize; } #endif for (UINT ismpd=0; ismpd<nsamples; ismpd++) { if ((samplemap[ismpd]) && (samplesize[ismpd]) && (dwMemPos < dwMemLength)) { ReadSample(&Ins[samplemap[ismpd]], flags[ismpd], (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); } dwMemPos += samplesize[ismpd]; if (dwMemPos >= dwMemLength) break; } } // Read song comments: "TEXT" if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x74786574)) { UINT len = *((DWORD *)(lpStream+dwMemPos+4)); dwMemPos += 8; if ((dwMemPos + len <= dwMemLength) && (len < 16384)) { m_lpszSongComments = new char[len+1]; if (m_lpszSongComments) { memcpy(m_lpszSongComments, lpStream+dwMemPos, len); m_lpszSongComments[len] = 0; } dwMemPos += len; } } // Read midi config: "MIDI" if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4944494D)) { UINT len = *((DWORD *)(lpStream+dwMemPos+4)); dwMemPos += 8; if (len == sizeof(MODMIDICFG)) { memcpy(&m_MidiCfg, lpStream+dwMemPos, len); m_dwSongFlags |= SONG_EMBEDMIDICFG; } } // Read pattern names: "PNAM" if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e50)) { UINT len = *((DWORD *)(lpStream+dwMemPos+4)); dwMemPos += 8; if ((dwMemPos + len <= dwMemLength) && (len <= MAX_PATTERNS*MAX_PATTERNNAME) && (len >= MAX_PATTERNNAME)) { m_lpszPatternNames = new char[len]; if (m_lpszPatternNames) { m_nPatternNames = len / MAX_PATTERNNAME; memcpy(m_lpszPatternNames, lpStream+dwMemPos, len); } dwMemPos += len; } } // Read channel names: "CNAM" if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e43)) { UINT len = *((DWORD *)(lpStream+dwMemPos+4)); dwMemPos += 8; if ((dwMemPos + len <= dwMemLength) && (len <= MAX_BASECHANNELS*MAX_CHANNELNAME)) { UINT n = len / MAX_CHANNELNAME; for (UINT i=0; i<n; i++) { memcpy(ChnSettings[i].szName, (lpStream+dwMemPos+i*MAX_CHANNELNAME), MAX_CHANNELNAME); ChnSettings[i].szName[MAX_CHANNELNAME-1] = 0; } dwMemPos += len; } } // Read mix plugins information if (dwMemPos + 8 < dwMemLength) { dwMemPos += LoadMixPlugins(lpStream+dwMemPos, dwMemLength-dwMemPos); } return TRUE; } #ifndef MODPLUG_NO_FILESAVE BOOL CSoundFile::SaveXM(LPCSTR lpszFileName, UINT nPacking) //--------------------------------------------------------- { BYTE s[64*64*5]; XMFILEHEADER header; XMINSTRUMENTHEADER xmih; XMSAMPLEHEADER xmsh; XMSAMPLESTRUCT xmss; BYTE smptable[32]; BYTE xmph[9]; FILE *f; int i; if ((!m_nChannels) || (!lpszFileName)) return FALSE; if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; fwrite("Extended Module: ", 17, 1, f); fwrite(m_szNames[0], 20, 1, f); s[0] = 0x1A; lstrcpy((LPSTR)&s[1], (nPacking) ? "MOD Plugin packed " : "FastTracker v2.00 "); s[21] = 0x04; s[22] = 0x01; fwrite(s, 23, 1, f); // Writing song header memset(&header, 0, sizeof(header)); header.size = sizeof(XMFILEHEADER); header.norder = 0; header.restartpos = m_nRestartPos; header.channels = m_nChannels; header.patterns = 0; for (i=0; i<MAX_ORDERS; i++) { if (Order[i] == 0xFF) break; header.norder++; if ((Order[i] >= header.patterns) && (Order[i] < MAX_PATTERNS)) header.patterns = Order[i]+1; } header.instruments = m_nInstruments; if (!header.instruments) header.instruments = m_nSamples; header.flags = (m_dwSongFlags & SONG_LINEARSLIDES) ? 0x01 : 0x00; if (m_dwSongFlags & SONG_EXFILTERRANGE) header.flags |= 0x1000; header.tempo = m_nDefaultTempo; header.speed = m_nDefaultSpeed; memcpy(header.order, Order, header.norder); fwrite(&header, 1, sizeof(header), f); // Writing patterns for (i=0; i<header.patterns; i++) if (Patterns[i]) { MODCOMMAND *p = Patterns[i]; UINT len = 0; memset(&xmph, 0, sizeof(xmph)); xmph[0] = 9; xmph[5] = (BYTE)(PatternSize[i] & 0xFF); xmph[6] = (BYTE)(PatternSize[i] >> 8); for (UINT j=m_nChannels*PatternSize[i]; j; j--,p++) { UINT note = p->note; UINT param = ModSaveCommand(p, TRUE); UINT command = param >> 8; param &= 0xFF; if (note >= 0xFE) note = 97; else if ((note <= 12) || (note > 96+12)) note = 0; else note -= 12; UINT vol = 0; if (p->volcmd) { UINT volcmd = p->volcmd; switch(volcmd) { case VOLCMD_VOLUME: vol = 0x10 + p->vol; break; case VOLCMD_VOLSLIDEDOWN: vol = 0x60 + (p->vol & 0x0F); break; case VOLCMD_VOLSLIDEUP: vol = 0x70 + (p->vol & 0x0F); break; case VOLCMD_FINEVOLDOWN: vol = 0x80 + (p->vol & 0x0F); break; case VOLCMD_FINEVOLUP: vol = 0x90 + (p->vol & 0x0F); break; case VOLCMD_VIBRATOSPEED: vol = 0xA0 + (p->vol & 0x0F); break; case VOLCMD_VIBRATO: vol = 0xB0 + (p->vol & 0x0F); break; case VOLCMD_PANNING: vol = 0xC0 + (p->vol >> 2); if (vol > 0xCF) vol = 0xCF; break; case VOLCMD_PANSLIDELEFT: vol = 0xD0 + (p->vol & 0x0F); break; case VOLCMD_PANSLIDERIGHT: vol = 0xE0 + (p->vol & 0x0F); break; case VOLCMD_TONEPORTAMENTO: vol = 0xF0 + (p->vol & 0x0F); break; } } if ((note) && (p->instr) && (vol > 0x0F) && (command) && (param)) { s[len++] = note; s[len++] = p->instr; s[len++] = vol; s[len++] = command; s[len++] = param; } else { BYTE b = 0x80; if (note) b |= 0x01; if (p->instr) b |= 0x02; if (vol >= 0x10) b |= 0x04; if (command) b |= 0x08; if (param) b |= 0x10; s[len++] = b; if (b & 1) s[len++] = note; if (b & 2) s[len++] = p->instr; if (b & 4) s[len++] = vol; if (b & 8) s[len++] = command; if (b & 16) s[len++] = param; } if (len > sizeof(s) - 5) break; } xmph[7] = (BYTE)(len & 0xFF); xmph[8] = (BYTE)(len >> 8); fwrite(xmph, 1, 9, f); fwrite(s, 1, len, f); } else { memset(&xmph, 0, sizeof(xmph)); xmph[0] = 9; xmph[5] = (BYTE)(PatternSize[i] & 0xFF); xmph[6] = (BYTE)(PatternSize[i] >> 8); fwrite(xmph, 1, 9, f); } // Writing instruments for (i=1; i<=header.instruments; i++) { MODINSTRUMENT *pins; BYTE flags[32]; memset(&xmih, 0, sizeof(xmih)); memset(&xmsh, 0, sizeof(xmsh)); xmih.size = sizeof(xmih) + sizeof(xmsh); memcpy(xmih.name, m_szNames[i], 22); xmih.type = 0; xmih.samples = 0; if (m_nInstruments) { INSTRUMENTHEADER *penv = Headers[i]; if (penv) { memcpy(xmih.name, penv->name, 22); xmih.type = penv->nMidiProgram; xmsh.volfade = penv->nFadeOut; xmsh.vnum = (BYTE)penv->nVolEnv; xmsh.pnum = (BYTE)penv->nPanEnv; if (xmsh.vnum > 12) xmsh.vnum = 12; if (xmsh.pnum > 12) xmsh.pnum = 12; for (UINT ienv=0; ienv<12; ienv++) { xmsh.venv[ienv*2] = penv->VolPoints[ienv]; xmsh.venv[ienv*2+1] = penv->VolEnv[ienv]; xmsh.penv[ienv*2] = penv->PanPoints[ienv]; xmsh.penv[ienv*2+1] = penv->PanEnv[ienv]; } if (penv->dwFlags & ENV_VOLUME) xmsh.vtype |= 1; if (penv->dwFlags & ENV_VOLSUSTAIN) xmsh.vtype |= 2; if (penv->dwFlags & ENV_VOLLOOP) xmsh.vtype |= 4; if (penv->dwFlags & ENV_PANNING) xmsh.ptype |= 1; if (penv->dwFlags & ENV_PANSUSTAIN) xmsh.ptype |= 2; if (penv->dwFlags & ENV_PANLOOP) xmsh.ptype |= 4; xmsh.vsustain = (BYTE)penv->nVolSustainBegin; xmsh.vloops = (BYTE)penv->nVolLoopStart; xmsh.vloope = (BYTE)penv->nVolLoopEnd; xmsh.psustain = (BYTE)penv->nPanSustainBegin; xmsh.ploops = (BYTE)penv->nPanLoopStart; xmsh.ploope = (BYTE)penv->nPanLoopEnd; for (UINT j=0; j<96; j++) if (penv->Keyboard[j+12]) { UINT k; for (k=0; k<xmih.samples; k++) if (smptable[k] == penv->Keyboard[j+12]) break; if (k == xmih.samples) { smptable[xmih.samples++] = penv->Keyboard[j+12]; } if (xmih.samples >= 32) break; xmsh.snum[j] = k; } // xmsh.reserved2 = xmih.samples; } } else { xmih.samples = 1; // xmsh.reserved2 = 1; smptable[0] = i; } xmsh.shsize = (xmih.samples) ? 40 : 0; fwrite(&xmih, 1, sizeof(xmih), f); if (smptable[0]) { MODINSTRUMENT *pvib = &Ins[smptable[0]]; xmsh.vibtype = pvib->nVibType; xmsh.vibsweep = pvib->nVibSweep; xmsh.vibdepth = pvib->nVibDepth; xmsh.vibrate = pvib->nVibRate; } fwrite(&xmsh, 1, xmih.size - sizeof(xmih), f); if (!xmih.samples) continue; for (UINT ins=0; ins<xmih.samples; ins++) { memset(&xmss, 0, sizeof(xmss)); if (smptable[ins]) memcpy(xmss.name, m_szNames[smptable[ins]], 22); pins = &Ins[smptable[ins]]; xmss.samplen = pins->nLength; xmss.loopstart = pins->nLoopStart; xmss.looplen = pins->nLoopEnd - pins->nLoopStart; xmss.vol = pins->nVolume / 4; xmss.finetune = (char)pins->nFineTune; xmss.type = 0; if (pins->uFlags & CHN_LOOP) xmss.type = (pins->uFlags & CHN_PINGPONGLOOP) ? 2 : 1; flags[ins] = RS_PCM8D; #ifndef NO_PACKING if (nPacking) { if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking))) { flags[ins] = RS_ADPCM4; xmss.res = 0xAD; } } else #endif { if (pins->uFlags & CHN_16BIT) { flags[ins] = RS_PCM16D; xmss.type |= 0x10; xmss.looplen *= 2; xmss.loopstart *= 2; xmss.samplen *= 2; } if (pins->uFlags & CHN_STEREO) { flags[ins] = (pins->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; xmss.type |= 0x20; xmss.looplen *= 2; xmss.loopstart *= 2; xmss.samplen *= 2; } } xmss.pan = 255; if (pins->nPan < 256) xmss.pan = (BYTE)pins->nPan; xmss.relnote = (signed char)pins->RelativeTone; fwrite(&xmss, 1, xmsh.shsize, f); } for (UINT ismpd=0; ismpd<xmih.samples; ismpd++) { pins = &Ins[smptable[ismpd]]; if (pins->pSample) { #ifndef NO_PACKING if ((flags[ismpd] == RS_ADPCM4) && (xmih.samples>1)) CanPackSample((char *)pins->pSample, pins->nLength, nPacking); #endif // NO_PACKING WriteSample(f, pins, flags[ismpd]); } } } // Writing song comments if ((m_lpszSongComments) && (m_lpszSongComments[0])) { DWORD d = 0x74786574; fwrite(&d, 1, 4, f); d = strlen(m_lpszSongComments); fwrite(&d, 1, 4, f); fwrite(m_lpszSongComments, 1, d, f); } // Writing midi cfg if (m_dwSongFlags & SONG_EMBEDMIDICFG) { DWORD d = 0x4944494D; fwrite(&d, 1, 4, f); d = sizeof(MODMIDICFG); fwrite(&d, 1, 4, f); fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f); } // Writing Pattern Names if ((m_nPatternNames) && (m_lpszPatternNames)) { DWORD dwLen = m_nPatternNames * MAX_PATTERNNAME; while ((dwLen >= MAX_PATTERNNAME) && (!m_lpszPatternNames[dwLen-MAX_PATTERNNAME])) dwLen -= MAX_PATTERNNAME; if (dwLen >= MAX_PATTERNNAME) { DWORD d = 0x4d414e50; fwrite(&d, 1, 4, f); fwrite(&dwLen, 1, 4, f); fwrite(m_lpszPatternNames, 1, dwLen, f); } } // Writing Channel Names { UINT nChnNames = 0; for (UINT inam=0; inam<m_nChannels; inam++) { if (ChnSettings[inam].szName[0]) nChnNames = inam+1; } // Do it! if (nChnNames) { DWORD dwLen = nChnNames * MAX_CHANNELNAME; DWORD d = 0x4d414e43; fwrite(&d, 1, 4, f); fwrite(&dwLen, 1, 4, f); for (UINT inam=0; inam<nChnNames; inam++) { fwrite(ChnSettings[inam].szName, 1, MAX_CHANNELNAME, f); } } } // Save mix plugins information SaveMixPlugins(f); fclose(f); return TRUE; } #endif // MODPLUG_NO_FILESAVE