1 /*
2  * This source code is public domain.
3  *
4  * Authors: Olivier Lapicque <olivierl@jps.net>,
5  *          Adam Goode       <adam@evdebs.org> (endian and char fixes for PPC)
6 */
7 
8 ////////////////////////////////////////////////////////////
9 // 669 Composer / UNIS 669 module loader
10 ////////////////////////////////////////////////////////////
11 
12 #include "stdafx.h"
13 #include "sndfile.h"
14 
15 //#pragma warning(disable:4244)
16 
17 typedef struct tagFILEHEADER669
18 {
19 	WORD sig;				// 'if' or 'JN'
20         signed char songmessage[108];	// Song Message
21 	BYTE samples;			// number of samples (1-64)
22 	BYTE patterns;			// number of patterns (1-128)
23 	BYTE restartpos;
24 	BYTE orders[128];
25 	BYTE tempolist[128];
26 	BYTE breaks[128];
27 } FILEHEADER669;
28 
29 
30 typedef struct tagSAMPLE669
31 {
32 	BYTE filename[13];
33 	BYTE length[4];	// when will somebody think about DWORD align ???
34 	BYTE loopstart[4];
35 	BYTE loopend[4];
36 } SAMPLE669;
37 
lengthArrayToDWORD(const BYTE length[4])38 DWORD lengthArrayToDWORD(const BYTE length[4]) {
39 	DWORD len = (length[3] << 24) +
40 		(length[2] << 16) +
41 		(length[1] << 8) +
42 		(length[0]);
43 
44 	return(len);
45 }
46 
47 
Read669(const BYTE * lpStream,DWORD dwMemLength)48 BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength)
49 //---------------------------------------------------------------
50 {
51 	BOOL b669Ext;
52 	const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
53 	const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
54 	DWORD dwMemPos = 0;
55 
56 	if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE;
57 	if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE;
58 	b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
59 	if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
60 	 || (!pfh->patterns) || (pfh->patterns > 128)) return FALSE;
61 	DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
62 	if (dontfuckwithme > dwMemLength) return FALSE;
63 	for (UINT ichk=0; ichk<pfh->samples; ichk++)
64 	{
65 		DWORD len = lengthArrayToDWORD(psmp[ichk].length);
66 		dontfuckwithme += len;
67 	}
68 	if (dontfuckwithme > dwMemLength) return FALSE;
69 	// That should be enough checking: this must be a 669 module.
70 	m_nType = MOD_TYPE_669;
71 	m_dwSongFlags |= SONG_LINEARSLIDES;
72 	m_nMinPeriod = 28 << 2;
73 	m_nMaxPeriod = 1712 << 3;
74 	m_nDefaultTempo = 125;
75 	m_nDefaultSpeed = 6;
76 	m_nChannels = 8;
77 	memcpy(m_szNames[0], pfh->songmessage, 16);
78 	m_nSamples = pfh->samples;
79 	for (UINT nins=1; nins<=m_nSamples; nins++, psmp++)
80 	{
81 		DWORD len = lengthArrayToDWORD(psmp->length);
82 		DWORD loopstart = lengthArrayToDWORD(psmp->loopstart);
83 		DWORD loopend = lengthArrayToDWORD(psmp->loopend);
84 		if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
85 		if ((loopend > len) && (!loopstart)) loopend = 0;
86 		if (loopend > len) loopend = len;
87 		if (loopstart + 4 >= loopend) loopstart = loopend = 0;
88 		Ins[nins].nLength = len;
89 		Ins[nins].nLoopStart = loopstart;
90 		Ins[nins].nLoopEnd = loopend;
91 		if (loopend) Ins[nins].uFlags |= CHN_LOOP;
92 		memcpy(m_szNames[nins], psmp->filename, 13);
93 		Ins[nins].nVolume = 256;
94 		Ins[nins].nGlobalVol = 64;
95 		Ins[nins].nPan = 128;
96 	}
97 	// Song Message
98 	m_lpszSongComments = new char[109];
99 	memcpy(m_lpszSongComments, pfh->songmessage, 108);
100 	m_lpszSongComments[108] = 0;
101 	// Reading Orders
102 	memcpy(Order, pfh->orders, 128);
103 	m_nRestartPos = pfh->restartpos;
104 	if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
105 	// Reading Pattern Break Locations
106 	for (UINT npan=0; npan<8; npan++)
107 	{
108 		ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0;
109 		ChnSettings[npan].nVolume = 64;
110 	}
111 	// Reading Patterns
112 	dwMemPos = 0x1F1 + pfh->samples * 25;
113 	for (UINT npat=0; npat<pfh->patterns; npat++)
114 	{
115 		Patterns[npat] = AllocatePattern(64, m_nChannels);
116 		if (!Patterns[npat]) break;
117 		PatternSize[npat] = 64;
118 		MODCOMMAND *m = Patterns[npat];
119 		const BYTE *p = lpStream + dwMemPos;
120 		for (UINT row=0; row<64; row++)
121 		{
122 			MODCOMMAND *mspeed = m;
123 			if ((row == pfh->breaks[npat]) && (row != 63))
124 			{
125 				for (UINT i=0; i<8; i++)
126 				{
127 					m[i].command = CMD_PATTERNBREAK;
128 					m[i].param = 0;
129 				}
130 			}
131 			for (UINT n=0; n<8; n++, m++, p+=3)
132 			{
133 				UINT note = p[0] >> 2;
134 				UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
135 				UINT vol = p[1] & 0x0F;
136 				if (p[0] < 0xFE)
137 				{
138 					m->note = note + 37;
139 					m->instr = instr + 1;
140 				}
141 				if (p[0] <= 0xFE)
142 				{
143 					m->volcmd = VOLCMD_VOLUME;
144 					m->vol = (vol << 2) + 2;
145 				}
146 				if (p[2] != 0xFF)
147 				{
148 					UINT command = p[2] >> 4;
149 					UINT param = p[2] & 0x0F;
150 					switch(command)
151 					{
152 					case 0x00:	command = CMD_PORTAMENTOUP; break;
153 					case 0x01:	command = CMD_PORTAMENTODOWN; break;
154 					case 0x02:	command = CMD_TONEPORTAMENTO; break;
155 					case 0x03:	command = CMD_MODCMDEX; param |= 0x50; break;
156 					case 0x04:	command = CMD_VIBRATO; param |= 0x40; break;
157 					case 0x05:	if (param) command = CMD_SPEED; else command = 0; param += 2; break;
158 					case 0x06:	if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; }
159 								else if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; }
160 								else command = 0;
161 								break;
162 					default:	command = 0;
163 					}
164 					if (command)
165 					{
166 						if (command == CMD_SPEED) mspeed = NULL;
167 						m->command = command;
168 						m->param = param;
169 					}
170 				}
171 			}
172 			if ((!row) && (mspeed))
173 			{
174 				for (UINT i=0; i<8; i++) if (!mspeed[i].command)
175 				{
176 					mspeed[i].command = CMD_SPEED;
177 					mspeed[i].param = pfh->tempolist[npat] + 2;
178 					break;
179 				}
180 			}
181 		}
182 		dwMemPos += 0x600;
183 	}
184 	// Reading Samples
185 	for (UINT n=1; n<=m_nSamples; n++)
186 	{
187 		UINT len = Ins[n].nLength;
188 		if (dwMemPos >= dwMemLength) break;
189 		if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
190 		dwMemPos += len;
191 	}
192 	return TRUE;
193 }
194