From 2342d48be31847e7ead9d1cc682452e8f0122351 Mon Sep 17 00:00:00 2001 From: llornkcor Date: Sat, 20 Jul 2002 22:07:31 +0000 Subject: initial commit of modplugin --- (limited to 'core/multimedia/opieplayer/modplug/load_ams.cpp') diff --git a/core/multimedia/opieplayer/modplug/load_ams.cpp b/core/multimedia/opieplayer/modplug/load_ams.cpp new file mode 100644 index 0000000..3dd1c2b --- a/dev/null +++ b/core/multimedia/opieplayer/modplug/load_ams.cpp @@ -0,0 +1,631 @@ +/* + * 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