mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-13 03:46:34 +00:00
629 lines
16 KiB
C++
629 lines
16 KiB
C++
|
/*
|
||
|
* This source code is public domain.
|
||
|
*
|
||
|
* Authors: Olivier Lapicque <olivierl@jps.net>
|
||
|
*/
|
||
|
|
||
|
//////////////////////////////////////////////
|
||
|
// 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;
|
||
|
} 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;
|
||
|
} 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<m_nChannels; cNam++)
|
||
|
{
|
||
|
if (dwMemPos + 32 >= 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; iOrd<pfh->orders; iOrd++, dwMemPos += 2)
|
||
|
{
|
||
|
UINT n = *((WORD *)(lpStream+dwMemPos));
|
||
|
Order[iOrd] = (BYTE)n;
|
||
|
}
|
||
|
// Read Patterns
|
||
|
for (UINT iPat=0; iPat<pfh->patterns; 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]
|
||
|
} AMS2FILEHEADER;
|
||
|
|
||
|
typedef struct AMS2SONGHEADER
|
||
|
{
|
||
|
WORD version;
|
||
|
BYTE instruments;
|
||
|
WORD patterns;
|
||
|
WORD orders;
|
||
|
WORD bpm;
|
||
|
BYTE speed;
|
||
|
BYTE channels;
|
||
|
BYTE commands;
|
||
|
BYTE rows;
|
||
|
WORD flags;
|
||
|
} AMS2SONGHEADER;
|
||
|
|
||
|
typedef struct AMS2INSTRUMENT
|
||
|
{
|
||
|
BYTE samples;
|
||
|
BYTE notemap[120];
|
||
|
} AMS2INSTRUMENT;
|
||
|
|
||
|
typedef struct AMS2ENVELOPE
|
||
|
{
|
||
|
BYTE speed;
|
||
|
BYTE sustain;
|
||
|
BYTE loopbegin;
|
||
|
BYTE loopend;
|
||
|
BYTE points;
|
||
|
BYTE info[3];
|
||
|
} AMS2ENVELOPE;
|
||
|
|
||
|
typedef struct AMS2SAMPLE
|
||
|
{
|
||
|
DWORD length;
|
||
|
DWORD loopstart;
|
||
|
DWORD loopend;
|
||
|
WORD frequency;
|
||
|
BYTE finetune;
|
||
|
WORD c4speed;
|
||
|
CHAR transpose;
|
||
|
BYTE volume;
|
||
|
BYTE flags;
|
||
|
} 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; ismpmap<pins->samples; 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; i<penv->nVolEnv; 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; ismp<pins->samples; 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<MAX_ORDERS; i++)
|
||
|
{
|
||
|
Order[i] = 0xFF;
|
||
|
if (dwMemPos + 2 >= dwMemLength) return TRUE;
|
||
|
if (i < psh->orders)
|
||
|
{
|
||
|
Order[i] = lpStream[dwMemPos];
|
||
|
dwMemPos += 2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Pattern Data
|
||
|
for (UINT ipat=0; ipat<psh->patterns; 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<dmax; i++)
|
||
|
{
|
||
|
BYTE al = *p++;
|
||
|
dh = 0;
|
||
|
for (UINT count=0; count<8; count++)
|
||
|
{
|
||
|
UINT bl = al & bitcount;
|
||
|
bl = ((bl|(bl<<8)) >> ((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<dmax; i++)
|
||
|
{
|
||
|
int pos = ((LPBYTE)pdest)[i];
|
||
|
if ((pos != 128) && (pos & 0x80)) pos = -(pos & 0x7F);
|
||
|
old -= (signed char)pos;
|
||
|
pdest[i] = old;
|
||
|
}
|
||
|
}
|
||
|
delete amstmp;
|
||
|
}
|
||
|
|