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 #define ULT_16BIT   0x04
13 #define ULT_LOOP    0x08
14 #define ULT_BIDI    0x10
15 
16 #pragma pack(1)
17 
18 // Raw ULT header struct:
19 typedef struct tagULTHEADER
20 {
21         char id[15];             // changed from CHAR
22         char songtitle[32];      // changed from CHAR
23 	BYTE reserved;
24 } ULTHEADER;
25 
26 
27 // Raw ULT sampleinfo struct:
28 typedef struct tagULTSAMPLE
29 {
30 	CHAR samplename[32];
31 	CHAR dosname[12];
32 	LONG loopstart;
33 	LONG loopend;
34 	LONG sizestart;
35 	LONG sizeend;
36 	BYTE volume;
37 	BYTE flags;
38 	WORD finetune;
39 } ULTSAMPLE;
40 
41 #pragma pack()
42 
43 
ReadUlt(const BYTE * lpStream,DWORD dwMemLength)44 BOOL CSoundFile::ReadUlt(const BYTE *lpStream, DWORD dwMemLength)
45 //---------------------------------------------------------------
46 {
47 	ULTHEADER *pmh = (ULTHEADER *)lpStream;
48 	ULTSAMPLE *pus;
49 	UINT nos, nop;
50 	DWORD dwMemPos = 0;
51 
52 	// try to read module header
53 	if ((!lpStream) || (dwMemLength < 0x100)) return FALSE;
54 	if (strncmp(pmh->id,"MAS_UTrack_V00",14)) return FALSE;
55 	// Warning! Not supported ULT format, trying anyway
56 	// if ((pmh->id[14] < '1') || (pmh->id[14] > '4')) return FALSE;
57 	m_nType = MOD_TYPE_ULT;
58 	m_nDefaultSpeed = 6;
59 	m_nDefaultTempo = 125;
60 	memcpy(m_szNames[0], pmh->songtitle, 32);
61 	m_szNames[0][31] = '\0';
62 	// read songtext
63 	dwMemPos = sizeof(ULTHEADER);
64 	if ((pmh->reserved) && (dwMemPos + pmh->reserved * 32 < dwMemLength))
65 	{
66 		UINT len = pmh->reserved * 32;
67 		m_lpszSongComments = new char[len + 1 + pmh->reserved];
68 		if (m_lpszSongComments)
69 		{
70 			for (UINT l=0; l<pmh->reserved; l++)
71 			{
72 				memcpy(m_lpszSongComments+l*33, lpStream+dwMemPos+l*32, 32);
73 				m_lpszSongComments[l*33+32] = 0x0D;
74 			}
75 			m_lpszSongComments[len] = 0;
76 		}
77 		dwMemPos += len;
78 	}
79 	if (dwMemPos >= dwMemLength) return TRUE;
80 	nos = lpStream[dwMemPos++];
81 	m_nSamples = nos;
82 	if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
83 	UINT smpsize = 64;
84 	if (pmh->id[14] >= '4')	smpsize += 2;
85 	if (dwMemPos + nos*smpsize + 256 + 2 > dwMemLength) return TRUE;
86 	for (UINT ins=1; ins<=nos; ins++, dwMemPos+=smpsize) if (ins<=m_nSamples)
87 	{
88 		pus	= (ULTSAMPLE *)(lpStream+dwMemPos);
89 		MODINSTRUMENT *pins = &Ins[ins];
90 		memcpy(m_szNames[ins], pus->samplename, 32);
91 		m_szNames[ins][31] = '\0';
92 		memcpy(pins->name, pus->dosname, 12);
93 		pins->nLoopStart = pus->loopstart;
94 		pins->nLoopEnd = pus->loopend;
95 		pins->nLength = pus->sizeend - pus->sizestart;
96 		pins->nVolume = pus->volume;
97 		pins->nGlobalVol = 64;
98 		pins->nC4Speed = 8363;
99 		if (pmh->id[14] >= '4')
100 		{
101 			pins->nC4Speed = pus->finetune;
102 		}
103 		if (pus->flags & ULT_LOOP) pins->uFlags |= CHN_LOOP;
104 		if (pus->flags & ULT_BIDI) pins->uFlags |= CHN_PINGPONGLOOP;
105 		if (pus->flags & ULT_16BIT)
106 		{
107 			pins->uFlags |= CHN_16BIT;
108 			pins->nLoopStart >>= 1;
109 			pins->nLoopEnd >>= 1;
110 		}
111 	}
112 	memcpy(Order, lpStream+dwMemPos, 256);
113 	dwMemPos += 256;
114 	m_nChannels = lpStream[dwMemPos] + 1;
115 	nop = lpStream[dwMemPos+1] + 1;
116 	dwMemPos += 2;
117 	if (m_nChannels > 32) m_nChannels = 32;
118 	// Default channel settings
119 	for (UINT nSet=0; nSet<m_nChannels; nSet++)
120 	{
121 		ChnSettings[nSet].nVolume = 64;
122 		ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
123 	}
124 	// read pan position table for v1.5 and higher
125 	if(pmh->id[14]>='3')
126 	{
127 		if (dwMemPos + m_nChannels > dwMemLength) return TRUE;
128 		for(UINT t=0; t<m_nChannels; t++)
129 		{
130 			ChnSettings[t].nPan = (lpStream[dwMemPos++] << 4) + 8;
131 			if (ChnSettings[t].nPan > 256) ChnSettings[t].nPan = 256;
132 		}
133 	}
134 	// Allocating Patterns
135 	for (UINT nAllocPat=0; nAllocPat<nop; nAllocPat++)
136 	{
137 		if (nAllocPat < MAX_PATTERNS)
138 		{
139 			PatternSize[nAllocPat] = 64;
140 			Patterns[nAllocPat] = AllocatePattern(64, m_nChannels);
141 		}
142 	}
143 	// Reading Patterns
144 	for (UINT nChn=0; nChn<m_nChannels; nChn++)
145 	{
146 		for (UINT nPat=0; nPat<nop; nPat++)
147 		{
148 			MODCOMMAND *pat = NULL;
149 
150 			if (nPat < MAX_PATTERNS)
151 			{
152 				pat = Patterns[nPat];
153 				if (pat) pat += nChn;
154 			}
155 			UINT row = 0;
156 			while (row < 64)
157 			{
158 				if (dwMemPos + 6 > dwMemLength) return TRUE;
159 				UINT rep = 1;
160 				UINT note = lpStream[dwMemPos++];
161 				if (note == 0xFC)
162 				{
163 					rep = lpStream[dwMemPos];
164 					note = lpStream[dwMemPos+1];
165 					dwMemPos += 2;
166 				}
167 				UINT instr = lpStream[dwMemPos++];
168 				UINT eff = lpStream[dwMemPos++];
169 				UINT dat1 = lpStream[dwMemPos++];
170 				UINT dat2 = lpStream[dwMemPos++];
171 				UINT cmd1 = eff & 0x0F;
172 				UINT cmd2 = eff >> 4;
173 				if (cmd1 == 0x0C) dat1 >>= 2; else
174 				if (cmd1 == 0x0B) { cmd1 = dat1 = 0; }
175 				if (cmd2 == 0x0C) dat2 >>= 2; else
176 				if (cmd2 == 0x0B) { cmd2 = dat2 = 0; }
177 				while ((rep != 0) && (row < 64))
178 				{
179 					if (pat)
180 					{
181 						pat->instr = instr;
182 						if (note) pat->note = note + 36;
183 						if (cmd1 | dat1)
184 						{
185 							if (cmd1 == 0x0C)
186 							{
187 								pat->volcmd = VOLCMD_VOLUME;
188 								pat->vol = dat1;
189 							} else
190 							{
191 								pat->command = cmd1;
192 								pat->param = dat1;
193 								ConvertModCommand(pat);
194 							}
195 						}
196 						if (cmd2 == 0x0C)
197 						{
198 							pat->volcmd = VOLCMD_VOLUME;
199 							pat->vol = dat2;
200 						} else
201 						if ((cmd2 | dat2) && (!pat->command))
202 						{
203 							pat->command = cmd2;
204 							pat->param = dat2;
205 							ConvertModCommand(pat);
206 						}
207 						pat += m_nChannels;
208 					}
209 					row++;
210 					rep--;
211 				}
212 			}
213 		}
214 	}
215 	// Reading Instruments
216 	for (UINT smp=1; smp<=m_nSamples; smp++) if (Ins[smp].nLength)
217 	{
218 		if (dwMemPos >= dwMemLength) return TRUE;
219 		UINT flags = (Ins[smp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
220 		dwMemPos += ReadSample(&Ins[smp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
221 	}
222 	return TRUE;
223 }
224 
225