gstreamer/gst/modplug/libmodplug/load_dsm.cpp

242 lines
5.6 KiB
C++
Raw Normal View History

/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
//////////////////////////////////////////////
// DSIK Internal Format (DSM) module loader //
//////////////////////////////////////////////
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "stdafx.h"
#include "sndfile.h"
#pragma pack(1)
#define DSMID_RIFF 0x46464952 // "RIFF"
#define DSMID_DSMF 0x464d5344 // "DSMF"
#define DSMID_SONG 0x474e4f53 // "SONG"
#define DSMID_INST 0x54534e49 // "INST"
#define DSMID_PATT 0x54544150 // "PATT"
typedef struct DSMNOTE
{
BYTE note,ins,vol,cmd,inf;
} DSMNOTE;
typedef struct DSMINST
{
DWORD id_INST;
DWORD inst_len;
CHAR filename[13];
BYTE flags;
BYTE flags2;
BYTE volume;
DWORD length;
DWORD loopstart;
DWORD loopend;
DWORD reserved1;
WORD c2spd;
WORD reserved2;
CHAR samplename[28];
} DSMINST;
typedef struct DSMFILEHEADER
{
DWORD id_RIFF; // "RIFF"
DWORD riff_len;
DWORD id_DSMF; // "DSMF"
DWORD id_SONG; // "SONG"
DWORD song_len;
} DSMFILEHEADER;
typedef struct DSMSONG
{
CHAR songname[28];
WORD reserved1;
WORD flags;
DWORD reserved2;
WORD numord;
WORD numsmp;
WORD numpat;
WORD numtrk;
BYTE globalvol;
BYTE mastervol;
BYTE speed;
BYTE bpm;
BYTE panpos[16];
BYTE orders[128];
} DSMSONG;
typedef struct DSMPATT
{
DWORD id_PATT;
DWORD patt_len;
BYTE dummy1;
BYTE dummy2;
} DSMPATT;
#pragma pack()
BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
DSMFILEHEADER *pfh = (DSMFILEHEADER *)lpStream;
DSMSONG *psong;
DWORD dwMemPos;
UINT nPat, nSmp;
if ((!lpStream) || (dwMemLength < 1024) || (pfh->id_RIFF != DSMID_RIFF)
|| (pfh->riff_len + 8 > dwMemLength) || (pfh->riff_len < 1024)
|| (pfh->id_DSMF != DSMID_DSMF) || (pfh->id_SONG != DSMID_SONG)
|| (pfh->song_len > dwMemLength)) return FALSE;
psong = (DSMSONG *)(lpStream + sizeof(DSMFILEHEADER));
dwMemPos = sizeof(DSMFILEHEADER) + pfh->song_len;
m_nType = MOD_TYPE_DSM;
m_nChannels = psong->numtrk;
if (m_nChannels < 4) m_nChannels = 4;
if (m_nChannels > 16) m_nChannels = 16;
m_nSamples = psong->numsmp;
if (m_nSamples > MAX_SAMPLES) m_nSamples = MAX_SAMPLES;
m_nDefaultSpeed = psong->speed;
m_nDefaultTempo = psong->bpm;
m_nDefaultGlobalVolume = psong->globalvol << 2;
if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
m_nSongPreAmp = psong->mastervol & 0x7F;
for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++)
{
Order[iOrd] = (BYTE)((iOrd < psong->numord) ? psong->orders[iOrd] : 0xFF);
}
for (UINT iPan=0; iPan<16; iPan++)
{
ChnSettings[iPan].nPan = 0x80;
if (psong->panpos[iPan] <= 0x80)
{
ChnSettings[iPan].nPan = psong->panpos[iPan] << 1;
}
}
memcpy(m_szNames[0], psong->songname, 28);
nPat = 0;
nSmp = 1;
while (dwMemPos < dwMemLength - 8)
{
DSMPATT *ppatt = (DSMPATT *)(lpStream + dwMemPos);
DSMINST *pins = (DSMINST *)(lpStream+dwMemPos);
// Reading Patterns
if (ppatt->id_PATT == DSMID_PATT)
{
dwMemPos += 8;
if (dwMemPos + ppatt->patt_len >= dwMemLength) break;
DWORD dwPos = dwMemPos;
dwMemPos += ppatt->patt_len;
MODCOMMAND *m = AllocatePattern(64, m_nChannels);
if (!m) break;
PatternSize[nPat] = 64;
Patterns[nPat] = m;
UINT row = 0;
while ((row < 64) && (dwPos + 2 <= dwMemPos))
{
UINT flag = lpStream[dwPos++];
if (flag)
{
UINT ch = (flag & 0x0F) % m_nChannels;
if (flag & 0x80)
{
UINT note = lpStream[dwPos++];
if (note)
{
if (note <= 12*9) note += 12;
m[ch].note = (BYTE)note;
}
}
if (flag & 0x40)
{
m[ch].instr = lpStream[dwPos++];
}
if (flag & 0x20)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = lpStream[dwPos++];
}
if (flag & 0x10)
{
UINT command = lpStream[dwPos++];
UINT param = lpStream[dwPos++];
switch(command)
{
// 4-bit Panning
case 0x08:
switch(param & 0xF0)
{
case 0x00: param <<= 4; break;
case 0x10: command = 0x0A; param = (param & 0x0F) << 4; break;
case 0x20: command = 0x0E; param = (param & 0x0F) | 0xA0; break;
case 0x30: command = 0x0E; param = (param & 0x0F) | 0x10; break;
case 0x40: command = 0x0E; param = (param & 0x0F) | 0x20; break;
default: command = 0;
}
break;
// Portamentos
case 0x11:
case 0x12:
command &= 0x0F;
break;
// 3D Sound (?)
case 0x13:
command = 'X' - 55;
param = 0x91;
break;
default:
// Volume + Offset (?)
command = ((command & 0xF0) == 0x20) ? 0x09 : 0;
}
m[ch].command = (BYTE)command;
m[ch].param = (BYTE)param;
if (command) ConvertModCommand(&m[ch]);
}
} else
{
m += m_nChannels;
row++;
}
}
nPat++;
} else
// Reading Samples
if ((nSmp <= m_nSamples) && (pins->id_INST == DSMID_INST))
{
if (dwMemPos + pins->inst_len >= dwMemLength - 8) break;
DWORD dwPos = dwMemPos + sizeof(DSMINST);
dwMemPos += 8 + pins->inst_len;
memcpy(m_szNames[nSmp], pins->samplename, 28);
MODINSTRUMENT *psmp = &Ins[nSmp];
memcpy(psmp->name, pins->filename, 13);
psmp->nGlobalVol = 64;
psmp->nC4Speed = pins->c2spd;
psmp->uFlags = (WORD)((pins->flags & 1) ? CHN_LOOP : 0);
psmp->nLength = pins->length;
psmp->nLoopStart = pins->loopstart;
psmp->nLoopEnd = pins->loopend;
psmp->nVolume = (WORD)(pins->volume << 2);
if (psmp->nVolume > 256) psmp->nVolume = 256;
UINT smptype = (pins->flags & 2) ? RS_PCM8S : RS_PCM8U;
ReadSample(psmp, smptype, (LPCSTR)(lpStream+dwPos), dwMemLength - dwPos);
nSmp++;
} else
{
break;
}
}
return TRUE;
}