1 /*
2  * This source code is public domain.
3  *
4  * Authors: Olivier Lapicque <olivierl@jps.net>
5 */
6 
7 #include "stdafx.h"
8 #include "sndfile.h"
9 
10 //#pragma warning(disable:4244)
11 
12 #pragma pack(1)
13 
14 typedef struct tagSTMNOTE
15 {
16 	BYTE note;
17 	BYTE insvol;
18 	BYTE volcmd;
19 	BYTE cmdinf;
20 } STMNOTE;
21 
22 
23 // Raw STM sampleinfo struct:
24 typedef struct tagSTMSAMPLE
25 {
26 	CHAR filename[14];	// Can't have long comments - just filename comments :)
27 	WORD reserved;		// ISA in memory when in ST 2
28 	WORD length;		// Sample length
29 	WORD loopbeg;		// Loop start point
30 	WORD loopend;		// Loop end point
31 	BYTE volume;		// Volume
32 	BYTE reserved2;		// More reserved crap
33 	WORD c2spd;			// Good old c2spd
34 	BYTE reserved3[6];	// Yet more of PSi's reserved crap
35 } STMSAMPLE;
36 
37 
38 // Raw STM header struct:
39 typedef struct tagSTMHEADER
40 {
41 	char songname[20];      // changed from CHAR
42 	char trackername[8];	// !SCREAM! for ST 2.xx  // changed from CHAR
43 	CHAR unused;			// 0x1A
44 	CHAR filetype;			// 1=song, 2=module (only 2 is supported, of course) :)
45 	CHAR ver_major;			// Like 2
46 	CHAR ver_minor;			// "ditto"
47 	BYTE inittempo;			// initspeed= stm inittempo>>4
48 	BYTE numpat;			// number of patterns
49 	BYTE globalvol;			// <- WoW! a RiGHT TRiANGLE =8*)
50 	BYTE reserved[13];		// More of PSi's internal crap
51 	STMSAMPLE sample[31];	// STM sample data
52 	BYTE patorder[128];		// Docs say 64 - actually 128
53 } STMHEADER;
54 
55 #pragma pack()
56 
57 
58 
ReadSTM(const BYTE * lpStream,DWORD dwMemLength)59 BOOL CSoundFile::ReadSTM(const BYTE *lpStream, DWORD dwMemLength)
60 //---------------------------------------------------------------
61 {
62 	const STMHEADER *phdr = (STMHEADER *)lpStream;
63 	DWORD dwMemPos = 0;
64 
65 	if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return FALSE;
66 	if ((phdr->filetype != 2) || (phdr->unused != 0x1A)
67 	 || ((strncmp(phdr->trackername, "!Scream!", 8))
68 	  && (strncmp(phdr->trackername, "BMOD2STM", 8)))) return FALSE;
69 	memcpy(m_szNames[0], phdr->songname, 20);
70 	// Read STM header
71 	m_nType = MOD_TYPE_STM;
72 	m_nSamples = 31;
73 	m_nChannels = 4;
74 	m_nInstruments = 0;
75 	m_nMinPeriod = 64;
76 	m_nMaxPeriod = 0x7FFF;
77 	m_nDefaultSpeed = phdr->inittempo >> 4;
78 	if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
79 	m_nDefaultTempo = 125;
80 	m_nDefaultGlobalVolume = phdr->globalvol << 2;
81 	if (m_nDefaultGlobalVolume > 256) m_nDefaultGlobalVolume = 256;
82 	memcpy(Order, phdr->patorder, 128);
83 	// Setting up channels
84 	for (UINT nSet=0; nSet<4; nSet++)
85 	{
86 		ChnSettings[nSet].dwFlags = 0;
87 		ChnSettings[nSet].nVolume = 64;
88 		ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
89 	}
90 	// Reading samples
91 	for (UINT nIns=0; nIns<31; nIns++)
92 	{
93 		MODINSTRUMENT *pIns = &Ins[nIns+1];
94 		const STMSAMPLE *pStm = &phdr->sample[nIns];  // STM sample data
95 		memcpy(pIns->name, pStm->filename, 13);
96 		memcpy(m_szNames[nIns+1], pStm->filename, 12);
97 		pIns->nC4Speed = bswapLE16(pStm->c2spd);
98 		pIns->nGlobalVol = 64;
99 		pIns->nVolume = pStm->volume << 2;
100 		if (pIns->nVolume > 256) pIns->nVolume = 256;
101 		pIns->nLength = bswapLE16(pStm->length);
102 		if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0;
103 		pIns->nLoopStart = bswapLE16(pStm->loopbeg);
104 		pIns->nLoopEnd = bswapLE16(pStm->loopend);
105 		if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF)) pIns->uFlags |= CHN_LOOP;
106 	}
107 	dwMemPos = sizeof(STMHEADER);
108 	for (UINT nOrd=0; nOrd<MAX_ORDERS; nOrd++) if (Order[nOrd] >= 99) Order[nOrd] = 0xFF;
109 	UINT nPatterns = phdr->numpat;
110 	for (UINT nPat=0; nPat<nPatterns; nPat++)
111 	{
112 		if (dwMemPos + 64*4*4 > dwMemLength) return TRUE;
113 		PatternSize[nPat] = 64;
114 		if ((Patterns[nPat] = AllocatePattern(64, m_nChannels)) == NULL) return TRUE;
115 		MODCOMMAND *m = Patterns[nPat];
116 		const STMNOTE *p = (const STMNOTE *)(lpStream + dwMemPos);
117 		for (UINT n=0; n<64*4; n++, p++, m++)
118 		{
119 			UINT note,ins,vol,cmd;
120 			// extract the various information from the 4 bytes that
121 			// make up a single note
122 			note = p->note;
123 			ins = p->insvol >> 3;
124 			vol = (p->insvol & 0x07) + (p->volcmd >> 1);
125 			cmd = p->volcmd & 0x0F;
126 			if ((ins) && (ins < 32)) m->instr = ins;
127 			// special values of [SBYTE0] are handled here ->
128 			// we have no idea if these strange values will ever be encountered
129 			// but it appears as though stms sound correct.
130 			if ((note == 0xFE) || (note == 0xFC)) m->note = 0xFE; else
131 			// if note < 251, then all three bytes are stored in the file
132 			if (note < 0xFC) m->note = (note >> 4)*12 + (note&0xf) + 37;
133 			if (vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; }
134 			m->param = p->cmdinf;
135 			switch(cmd)
136 			{
137 			// Axx set speed to xx
138 			case 1:	m->command = CMD_SPEED; m->param >>= 4; break;
139 			// Bxx position jump
140 			case 2:	m->command = CMD_POSITIONJUMP; break;
141 			// Cxx patternbreak to row xx
142 			case 3:	m->command = CMD_PATTERNBREAK; m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F);	break;
143 			// Dxy volumeslide
144 			case 4:	m->command = CMD_VOLUMESLIDE; break;
145 			// Exy toneslide down
146 			case 5:	m->command = CMD_PORTAMENTODOWN; break;
147 			// Fxy toneslide up
148 			case 6:	m->command = CMD_PORTAMENTOUP; break;
149 			// Gxx Tone portamento,speed xx
150 			case 7:	m->command = CMD_TONEPORTAMENTO; break;
151 			// Hxy vibrato
152 			case 8:	m->command = CMD_VIBRATO; break;
153 			// Ixy tremor, ontime x, offtime y
154 			case 9:	m->command = CMD_TREMOR; break;
155 			// Jxy arpeggio
156 			case 10: m->command = CMD_ARPEGGIO; break;
157 			// Kxy Dual command H00 & Dxy
158 			case 11: m->command = CMD_VIBRATOVOL; break;
159 			// Lxy Dual command G00 & Dxy
160 			case 12: m->command = CMD_TONEPORTAVOL; break;
161 			// Xxx amiga command 8xx
162 			case 0x18:	m->command = CMD_PANNING8; break;
163 			default:
164 				m->command = m->param = 0;
165 			}
166 		}
167 		dwMemPos += 64*4*4;
168 	}
169 	// Reading Samples
170 	for (UINT nSmp=1; nSmp<=31; nSmp++)
171 	{
172 		MODINSTRUMENT *pIns = &Ins[nSmp];
173 		dwMemPos = (dwMemPos + 15) & (~15);
174 		if (pIns->nLength)
175 		{
176 			UINT nPos = ((UINT)phdr->sample[nSmp-1].reserved) << 4;
177 			if ((nPos >= sizeof(STMHEADER)) && (nPos+pIns->nLength <= dwMemLength)) dwMemPos = nPos;
178 			if (dwMemPos < dwMemLength)
179 			{
180 				dwMemPos += ReadSample(pIns, RS_PCM8S, (LPSTR)(lpStream+dwMemPos),dwMemLength-dwMemPos);
181 			}
182 		}
183 	}
184 	return TRUE;
185 }
186 
187