/* * This program is free software; you can redistribute it and modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the license or (at your * option) any later version. * * Authors: Olivier Lapicque */ ////////////////////////////////////////////// // AMS module loader // ////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" //#pragma warning(disable:4244) #pragma pack(1) typedef struct AMSFILEHEADER { char szHeader[7]; // "Extreme" // changed from CHAR BYTE verlo, verhi; // 0x??,0x01 BYTE chncfg; BYTE samples; WORD patterns; WORD orders; BYTE vmidi; WORD extra; } Q_PACKED AMSFILEHEADER; typedef struct AMSSAMPLEHEADER { DWORD length; DWORD loopstart; DWORD loopend; BYTE finetune_and_pan; WORD samplerate; // C-2 = 8363 BYTE volume; // 0-127 BYTE infobyte; } Q_PACKED AMSSAMPLEHEADER; #pragma pack() BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) //----------------------------------------------------------- { BYTE pkinf[MAX_SAMPLES]; AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream; DWORD dwMemPos; UINT tmp, tmp2; if ((!lpStream) || (dwMemLength < 1024)) return FALSE; if ((pfh->verhi != 0x01) || (strncmp(pfh->szHeader, "Extreme", 7)) || (!pfh->patterns) || (!pfh->orders) || (!pfh->samples) || (pfh->samples > MAX_SAMPLES) || (pfh->patterns > MAX_PATTERNS) || (pfh->orders > MAX_ORDERS)) { return ReadAMS2(lpStream, dwMemLength); } dwMemPos = sizeof(AMSFILEHEADER) + pfh->extra; if (dwMemPos + pfh->samples * sizeof(AMSSAMPLEHEADER) + 256 >= dwMemLength) return FALSE; m_nType = MOD_TYPE_AMS; m_nInstruments = 0; m_nChannels = (pfh->chncfg & 0x1F) + 1; m_nSamples = pfh->samples; for (UINT nSmp=1; nSmp<=m_nSamples; nSmp++, dwMemPos += sizeof(AMSSAMPLEHEADER)) { AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos); MODINSTRUMENT *pins = &Ins[nSmp]; pins->nLength = psh->length; pins->nLoopStart = psh->loopstart; pins->nLoopEnd = psh->loopend; pins->nGlobalVol = 64; pins->nVolume = psh->volume << 1; pins->nC4Speed = psh->samplerate; pins->nPan = (psh->finetune_and_pan & 0xF0); if (pins->nPan < 0x80) pins->nPan += 0x10; pins->nFineTune = MOD2XMFineTune(psh->finetune_and_pan & 0x0F); pins->uFlags = (psh->infobyte & 0x80) ? CHN_16BIT : 0; if ((pins->nLoopEnd <= pins->nLength) && (pins->nLoopStart+4 <= pins->nLoopEnd)) pins->uFlags |= CHN_LOOP; pkinf[nSmp] = psh->infobyte; } // Read Song Name tmp = lpStream[dwMemPos++]; if (dwMemPos + tmp + 1 >= dwMemLength) return TRUE; tmp2 = (tmp < 32) ? tmp : 31; if (tmp2) memcpy(m_szNames[0], lpStream+dwMemPos, tmp2); m_szNames[0][tmp2] = 0; dwMemPos += tmp; // Read sample names for (UINT sNam=1; sNam<=m_nSamples; sNam++) { if (dwMemPos + 32 >= dwMemLength) return TRUE; tmp = lpStream[dwMemPos++]; tmp2 = (tmp < 32) ? tmp : 31; if (tmp2) memcpy(m_szNames[sNam], lpStream+dwMemPos, tmp2); dwMemPos += tmp; } // Skip Channel names for (UINT cNam=0; cNam= dwMemLength) return TRUE; tmp = lpStream[dwMemPos++]; dwMemPos += tmp; } // Read Pattern Names m_lpszPatternNames = new char[pfh->patterns * 32]; // changed from CHAR if (!m_lpszPatternNames) return TRUE; m_nPatternNames = pfh->patterns; memset(m_lpszPatternNames, 0, m_nPatternNames * 32); for (UINT pNam=0; pNam < m_nPatternNames; pNam++) { if (dwMemPos + 32 >= dwMemLength) return TRUE; tmp = lpStream[dwMemPos++]; tmp2 = (tmp < 32) ? tmp : 31; if (tmp2) memcpy(m_lpszPatternNames+pNam*32, lpStream+dwMemPos, tmp2); dwMemPos += tmp; } // Read Song Comments tmp = *((WORD *)(lpStream+dwMemPos)); dwMemPos += 2; if (dwMemPos + tmp >= dwMemLength) return TRUE; if (tmp) { m_lpszSongComments = new char[tmp+1]; // changed from CHAR if (!m_lpszSongComments) return TRUE; memset(m_lpszSongComments, 0, tmp+1); memcpy(m_lpszSongComments, lpStream + dwMemPos, tmp); dwMemPos += tmp; } // Read Order List for (UINT iOrd=0; iOrdorders; iOrd++, dwMemPos += 2) { UINT n = *((WORD *)(lpStream+dwMemPos)); Order[iOrd] = (BYTE)n; } // Read Patterns for (UINT iPat=0; iPatpatterns; iPat++) { if (dwMemPos + 4 >= dwMemLength) return TRUE; UINT len = *((DWORD *)(lpStream + dwMemPos)); dwMemPos += 4; if ((len >= dwMemLength) || (dwMemPos + len > dwMemLength)) return TRUE; PatternSize[iPat] = 64; MODCOMMAND *m = AllocatePattern(PatternSize[iPat], m_nChannels); if (!m) return TRUE; Patterns[iPat] = m; const BYTE *p = lpStream + dwMemPos; UINT row = 0, i = 0; while ((row < PatternSize[iPat]) && (i+2 < len)) { BYTE b0 = p[i++]; BYTE b1 = p[i++]; BYTE b2 = 0; UINT ch = b0 & 0x3F; // Note+Instr if (!(b0 & 0x40)) { b2 = p[i++]; if (ch < m_nChannels) { if (b1 & 0x7F) m[ch].note = (b1 & 0x7F) + 25; m[ch].instr = b2; } if (b1 & 0x80) { b0 |= 0x40; b1 = p[i++]; } } // Effect if (b0 & 0x40) { anothercommand: if (b1 & 0x40) { if (ch < m_nChannels) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = b1 & 0x3F; } } else { b2 = p[i++]; if (ch < m_nChannels) { UINT cmd = b1 & 0x3F; if (cmd == 0x0C) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = b2 >> 1; } else if (cmd == 0x0E) { if (!m[ch].command) { UINT command = CMD_S3MCMDEX; UINT param = b2; switch(param & 0xF0) { case 0x00: if (param & 0x08) { param &= 0x07; param |= 0x90; } else {command=param=0;} break; case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break; case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break; case 0x30: param = (param & 0x0F) | 0x10; break; case 0x40: param = (param & 0x0F) | 0x30; break; case 0x50: param = (param & 0x0F) | 0x20; break; case 0x60: param = (param & 0x0F) | 0xB0; break; case 0x70: param = (param & 0x0F) | 0x40; break; case 0x90: command = CMD_RETRIG; param &= 0x0F; break; case 0xA0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command=param=0; break; case 0xB0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command=param=0; break; } m[ch].command = command; m[ch].param = param; } } else { m[ch].command = cmd; m[ch].param = b2; ConvertModCommand(&m[ch]); } } } if (b1 & 0x80) { b1 = p[i++]; if (i <= len) goto anothercommand; } } if (b0 & 0x80) { row++; m += m_nChannels; } } dwMemPos += len; } // Read Samples for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) { if (dwMemPos >= dwMemLength - 9) return TRUE; UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); } return TRUE; } ///////////////////////////////////////////////////////////////////// // AMS 2.2 loader #pragma pack(1) typedef struct AMS2FILEHEADER { DWORD dwHdr1; // AMShdr WORD wHdr2; BYTE b1A; // 0x1A BYTE titlelen; // 30-bytes max CHAR szTitle[30]; // [titlelen] } Q_PACKED AMS2FILEHEADER; typedef struct AMS2SONGHEADER { WORD version; BYTE instruments; WORD patterns; WORD orders; WORD bpm; BYTE speed; BYTE channels; BYTE commands; BYTE rows; WORD flags; } Q_PACKED AMS2SONGHEADER; typedef struct AMS2INSTRUMENT { BYTE samples; BYTE notemap[120]; } Q_PACKED AMS2INSTRUMENT; typedef struct AMS2ENVELOPE { BYTE speed; BYTE sustain; BYTE loopbegin; BYTE loopend; BYTE points; BYTE info[3]; } Q_PACKED AMS2ENVELOPE; typedef struct AMS2SAMPLE { DWORD length; DWORD loopstart; DWORD loopend; WORD frequency; BYTE finetune; WORD c4speed; CHAR transpose; BYTE volume; BYTE flags; } Q_PACKED AMS2SAMPLE; #pragma pack() BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) //------------------------------------------------------------ { AMS2FILEHEADER *pfh = (AMS2FILEHEADER *)lpStream; AMS2SONGHEADER *psh; DWORD dwMemPos; BYTE smpmap[16]; BYTE packedsamples[MAX_SAMPLES]; if ((pfh->dwHdr1 != 0x68534D41) || (pfh->wHdr2 != 0x7264) || (pfh->b1A != 0x1A) || (pfh->titlelen > 30)) return FALSE; dwMemPos = pfh->titlelen + 8; psh = (AMS2SONGHEADER *)(lpStream + dwMemPos); if (((psh->version & 0xFF00) != 0x0200) || (!psh->instruments) || (psh->instruments > MAX_INSTRUMENTS) || (!psh->patterns) || (!psh->orders)) return FALSE; dwMemPos += sizeof(AMS2SONGHEADER); if (pfh->titlelen) { memcpy(m_szNames, pfh->szTitle, pfh->titlelen); m_szNames[0][pfh->titlelen] = 0; } m_nType = MOD_TYPE_AMS; m_nChannels = 32; m_nDefaultTempo = psh->bpm >> 8; m_nDefaultSpeed = psh->speed; m_nInstruments = psh->instruments; m_nSamples = 0; if (psh->flags & 0x40) m_dwSongFlags |= SONG_LINEARSLIDES; for (UINT nIns=1; nIns<=m_nInstruments; nIns++) { UINT insnamelen = lpStream[dwMemPos]; CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1); dwMemPos += insnamelen + 1; AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos); dwMemPos += sizeof(AMS2INSTRUMENT); if (dwMemPos + 1024 >= dwMemLength) return TRUE; AMS2ENVELOPE *volenv, *panenv, *pitchenv; volenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); dwMemPos += 5 + volenv->points*3; panenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); dwMemPos += 5 + panenv->points*3; pitchenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); dwMemPos += 5 + pitchenv->points*3; INSTRUMENTHEADER *penv = new INSTRUMENTHEADER; if (!penv) return TRUE; memset(smpmap, 0, sizeof(smpmap)); memset(penv, 0, sizeof(INSTRUMENTHEADER)); for (UINT ismpmap=0; ismpmapsamples; ismpmap++) { if ((ismpmap >= 16) || (m_nSamples+1 >= MAX_SAMPLES)) break; m_nSamples++; smpmap[ismpmap] = m_nSamples; } penv->nGlobalVol = 64; penv->nPan = 128; penv->nPPC = 60; Headers[nIns] = penv; if (insnamelen) { if (insnamelen > 31) insnamelen = 31; memcpy(penv->name, pinsname, insnamelen); penv->name[insnamelen] = 0; } for (UINT inotemap=0; inotemap<120; inotemap++) { penv->NoteMap[inotemap] = inotemap+1; penv->Keyboard[inotemap] = smpmap[pins->notemap[inotemap] & 0x0F]; } // Volume Envelope { UINT pos = 0; penv->nVolEnv = (volenv->points > 16) ? 16 : volenv->points; penv->nVolSustainBegin = penv->nVolSustainEnd = volenv->sustain; penv->nVolLoopStart = volenv->loopbegin; penv->nVolLoopEnd = volenv->loopend; for (UINT i=0; inVolEnv; i++) { penv->VolEnv[i] = (BYTE)((volenv->info[i*3+2] & 0x7F) >> 1); pos += volenv->info[i*3] + ((volenv->info[i*3+1] & 1) << 8); penv->VolPoints[i] = (WORD)pos; } } penv->nFadeOut = (((lpStream[dwMemPos+2] & 0x0F) << 8) | (lpStream[dwMemPos+1])) << 3; UINT envflags = lpStream[dwMemPos+3]; if (envflags & 0x01) penv->dwFlags |= ENV_VOLLOOP; if (envflags & 0x02) penv->dwFlags |= ENV_VOLSUSTAIN; if (envflags & 0x04) penv->dwFlags |= ENV_VOLUME; dwMemPos += 5; // Read Samples for (UINT ismp=0; ismpsamples; ismp++) { MODINSTRUMENT *psmp = ((ismp < 16) && (smpmap[ismp])) ? &Ins[smpmap[ismp]] : NULL; UINT smpnamelen = lpStream[dwMemPos]; if ((psmp) && (smpnamelen) && (smpnamelen <= 22)) { memcpy(m_szNames[smpmap[ismp]], lpStream+dwMemPos+1, smpnamelen); } dwMemPos += smpnamelen + 1; if (psmp) { AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos); psmp->nGlobalVol = 64; psmp->nPan = 128; psmp->nLength = pams->length; psmp->nLoopStart = pams->loopstart; psmp->nLoopEnd = pams->loopend; psmp->nC4Speed = pams->c4speed; psmp->RelativeTone = pams->transpose; psmp->nVolume = pams->volume / 2; packedsamples[smpmap[ismp]] = pams->flags; if (pams->flags & 0x04) psmp->uFlags |= CHN_16BIT; if (pams->flags & 0x08) psmp->uFlags |= CHN_LOOP; if (pams->flags & 0x10) psmp->uFlags |= CHN_PINGPONGLOOP; } dwMemPos += sizeof(AMS2SAMPLE); } } if (dwMemPos + 256 >= dwMemLength) return TRUE; // Comments { UINT composernamelen = lpStream[dwMemPos]; if (composernamelen) { m_lpszSongComments = new char[composernamelen+1]; // changed from CHAR if (m_lpszSongComments) { memcpy(m_lpszSongComments, lpStream+dwMemPos+1, composernamelen); m_lpszSongComments[composernamelen] = 0; } } dwMemPos += composernamelen + 1; // channel names for (UINT i=0; i<32; i++) { UINT chnnamlen = lpStream[dwMemPos]; if ((chnnamlen) && (chnnamlen < MAX_CHANNELNAME)) { memcpy(ChnSettings[i].szName, lpStream+dwMemPos+1, chnnamlen); } dwMemPos += chnnamlen + 1; if (dwMemPos + chnnamlen + 256 >= dwMemLength) return TRUE; } // packed comments (ignored) UINT songtextlen = *((LPDWORD)(lpStream+dwMemPos)); dwMemPos += songtextlen; if (dwMemPos + 256 >= dwMemLength) return TRUE; } // Order List { for (UINT i=0; i= dwMemLength) return TRUE; if (i < psh->orders) { Order[i] = lpStream[dwMemPos]; dwMemPos += 2; } } } // Pattern Data for (UINT ipat=0; ipatpatterns; ipat++) { if (dwMemPos+8 >= dwMemLength) return TRUE; UINT packedlen = *((LPDWORD)(lpStream+dwMemPos)); UINT numrows = 1 + (UINT)(lpStream[dwMemPos+4]); //UINT patchn = 1 + (UINT)(lpStream[dwMemPos+5] & 0x1F); //UINT patcmds = 1 + (UINT)(lpStream[dwMemPos+5] >> 5); UINT patnamlen = lpStream[dwMemPos+6]; dwMemPos += 4; if ((ipat < MAX_PATTERNS) && (packedlen < dwMemLength-dwMemPos) && (numrows >= 8)) { if ((patnamlen) && (patnamlen < MAX_PATTERNNAME)) { char s[MAX_PATTERNNAME]; // changed from CHAR memcpy(s, lpStream+dwMemPos+3, patnamlen); s[patnamlen] = 0; SetPatternName(ipat, s); } PatternSize[ipat] = numrows; Patterns[ipat] = AllocatePattern(numrows, m_nChannels); if (!Patterns[ipat]) return TRUE; // Unpack Pattern Data LPCBYTE psrc = lpStream + dwMemPos; UINT pos = 3 + patnamlen; UINT row = 0; while ((pos < packedlen) && (row < numrows)) { MODCOMMAND *m = Patterns[ipat] + row * m_nChannels; UINT byte1 = psrc[pos++]; UINT ch = byte1 & 0x1F; // Read Note + Instr if (!(byte1 & 0x40)) { UINT byte2 = psrc[pos++]; UINT note = byte2 & 0x7F; if (note) m[ch].note = (note > 1) ? (note-1) : 0xFF; m[ch].instr = psrc[pos++]; // Read Effect while (byte2 & 0x80) { byte2 = psrc[pos++]; if (byte2 & 0x40) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = byte2 & 0x3F; } else { UINT command = byte2 & 0x3F; UINT param = psrc[pos++]; if (command == 0x0C) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = param / 2; } else if (command < 0x10) { m[ch].command = command; m[ch].param = param; ConvertModCommand(&m[ch]); } else { // TODO: AMS effects } } } } if (byte1 & 0x80) row++; } } dwMemPos += packedlen; } // Read Samples for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) { if (dwMemPos >= dwMemLength - 9) return TRUE; UINT flags; if (packedsamples[iSmp] & 0x03) { flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; } else { flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; } dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); } return TRUE; } ///////////////////////////////////////////////////////////////////// // AMS Sample unpacking void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter) { UINT tmplen = dmax; signed char *amstmp = new signed char[tmplen]; if (!amstmp) return; // Unpack Loop { signed char *p = amstmp; UINT i=0, j=0; while ((i < inputlen) && (j < tmplen)) { signed char ch = psrc[i++]; if (ch == packcharacter) { BYTE ch2 = psrc[i++]; if (ch2) { ch = psrc[i++]; while (ch2--) { p[j++] = ch; if (j >= tmplen) break; } } else p[j++] = packcharacter; } else p[j++] = ch; } } // Bit Unpack Loop { signed char *p = amstmp; UINT bitcount = 0x80, dh; UINT k=0; for (UINT i=0; i> ((dh+8-count) & 7)) & 0xFF; bitcount = ((bitcount|(bitcount<<8)) >> 1) & 0xFF; pdest[k++] |= bl; if (k >= dmax) { k = 0; dh++; } } bitcount = ((bitcount|(bitcount<<8)) >> dh) & 0xFF; } } // Delta Unpack { signed char old = 0; for (UINT i=0; i