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 #include "stdafx.h"
9 #include "sndfile.h"
10 #include "tables.h"
11 
12 #ifdef _MSC_VER
13 //#pragma warning(disable:4244)
14 #endif
15 
16 //////////////////////////////////////////////////////
17 // ScreamTracker S3M file support
18 
19 #pragma pack(1)
20 typedef struct tagS3MSAMPLESTRUCT
21 {
22 	BYTE type;
23 	CHAR dosname[12];
24 	BYTE hmem;
25 	WORD memseg;
26 	DWORD length;
27 	DWORD loopbegin;
28 	DWORD loopend;
29 	BYTE vol;
30 	BYTE bReserved;
31 	BYTE pack;
32 	BYTE flags;
33 	DWORD finetune;
34 	DWORD dwReserved;
35 	WORD intgp;
36 	WORD int512;
37 	DWORD lastused;
38 	CHAR name[28];
39 	CHAR scrs[4];
40 } S3MSAMPLESTRUCT;
41 
42 
43 typedef struct tagS3MFILEHEADER
44 {
45 	CHAR name[28];
46 	BYTE b1A;
47 	BYTE type;
48 	WORD reserved1;
49 	WORD ordnum;
50 	WORD insnum;
51 	WORD patnum;
52 	WORD flags;
53 	WORD cwtv;
54 	WORD version;
55 	DWORD scrm;	// "SCRM" = 0x4D524353
56 	BYTE globalvol;
57 	BYTE speed;
58 	BYTE tempo;
59 	BYTE mastervol;
60 	BYTE ultraclicks;
61 	BYTE panning_present;
62 	BYTE reserved2[8];
63 	WORD special;
64 	BYTE channels[32];
65 } S3MFILEHEADER;
66 
67 
68 void CSoundFile::S3MConvert(MODCOMMAND *m, BOOL bIT) const
69 //--------------------------------------------------------
70 {
71 	UINT command = m->command;
72 	UINT param = m->param;
73 	switch (command + 0x40)
74 	{
75 	case 'A':	command = CMD_SPEED; break;
76 	case 'B':	command = CMD_POSITIONJUMP; break;
77 	case 'C':	command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break;
78 	case 'D':	command = CMD_VOLUMESLIDE; break;
79 	case 'E':	command = CMD_PORTAMENTODOWN; break;
80 	case 'F':	command = CMD_PORTAMENTOUP; break;
81 	case 'G':	command = CMD_TONEPORTAMENTO; break;
82 	case 'H':	command = CMD_VIBRATO; break;
83 	case 'I':	command = CMD_TREMOR; break;
84 	case 'J':	command = CMD_ARPEGGIO; break;
85 	case 'K':	command = CMD_VIBRATOVOL; break;
86 	case 'L':	command = CMD_TONEPORTAVOL; break;
87 	case 'M':	command = CMD_CHANNELVOLUME; break;
88 	case 'N':	command = CMD_CHANNELVOLSLIDE; break;
89 	case 'O':	command = CMD_OFFSET; break;
90 	case 'P':	command = CMD_PANNINGSLIDE; break;
91 	case 'Q':	command = CMD_RETRIG; break;
92 	case 'R':	command = CMD_TREMOLO; break;
93 	case 'S':	command = CMD_S3MCMDEX; break;
94 	case 'T':	command = CMD_TEMPO; break;
95 	case 'U':	command = CMD_FINEVIBRATO; break;
96 	case 'V':	command = CMD_GLOBALVOLUME; break;
97 	case 'W':	command = CMD_GLOBALVOLSLIDE; break;
98 	case 'X':	command = CMD_PANNING8; break;
99 	case 'Y':	command = CMD_PANBRELLO; break;
100 	case 'Z':	command = CMD_MIDI; break;
101 	default:	command = 0;
102 	}
103 	m->command = command;
104 	m->param = param;
105 }
106 
107 
108 #ifndef MODPLUG_NO_FILESAVE
109 void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const
110 //---------------------------------------------------------------------
111 {
112 	UINT command = *pcmd;
113 	UINT param = *pprm;
114 	switch(command)
115 	{
116 	case CMD_SPEED:				command = 'A'; break;
117 	case CMD_POSITIONJUMP:		command = 'B'; break;
118 	case CMD_PATTERNBREAK:		command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break;
119 	case CMD_VOLUMESLIDE:		command = 'D'; break;
120 	case CMD_PORTAMENTODOWN:	command = 'E'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
121 	case CMD_PORTAMENTOUP:		command = 'F'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
122 	case CMD_TONEPORTAMENTO:	command = 'G'; break;
123 	case CMD_VIBRATO:			command = 'H'; break;
124 	case CMD_TREMOR:			command = 'I'; break;
125 	case CMD_ARPEGGIO:			command = 'J'; break;
126 	case CMD_VIBRATOVOL:		command = 'K'; break;
127 	case CMD_TONEPORTAVOL:		command = 'L'; break;
128 	case CMD_CHANNELVOLUME:		command = 'M'; break;
129 	case CMD_CHANNELVOLSLIDE:	command = 'N'; break;
130 	case CMD_OFFSET:			command = 'O'; break;
131 	case CMD_PANNINGSLIDE:		command = 'P'; break;
132 	case CMD_RETRIG:			command = 'Q'; break;
133 	case CMD_TREMOLO:			command = 'R'; break;
134 	case CMD_S3MCMDEX:			command = 'S'; break;
135 	case CMD_TEMPO:				command = 'T'; break;
136 	case CMD_FINEVIBRATO:		command = 'U'; break;
137 	case CMD_GLOBALVOLUME:		command = 'V'; break;
138 	case CMD_GLOBALVOLSLIDE:	command = 'W'; break;
139 	case CMD_PANNING8:
140 		command = 'X';
141 		if ((bIT) && (m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM))
142 		{
143 			if (param == 0xA4) { command = 'S'; param = 0x91; }	else
144 			if (param <= 0x80) { param <<= 1; if (param > 255) param = 255; } else
145 			command = param = 0;
146 		} else
147 		if ((!bIT) && ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)))
148 		{
149 			param >>= 1;
150 		}
151 		break;
152 	case CMD_PANBRELLO:			command = 'Y'; break;
153 	case CMD_MIDI:				command = 'Z'; break;
154 	case CMD_XFINEPORTAUPDOWN:
155 		if (param & 0x0F) switch(param & 0xF0)
156 		{
157 		case 0x10:	command = 'F'; param = (param & 0x0F) | 0xE0; break;
158 		case 0x20:	command = 'E'; param = (param & 0x0F) | 0xE0; break;
159 		case 0x90:	command = 'S'; break;
160 		default:	command = param = 0;
161 		} else command = param = 0;
162 		break;
163 	case CMD_MODCMDEX:
164 		command = 'S';
165 		switch(param & 0xF0)
166 		{
167 		case 0x00:	command = param = 0; break;
168 		case 0x10:	command = 'F'; param |= 0xF0; break;
169 		case 0x20:	command = 'E'; param |= 0xF0; break;
170 		case 0x30:	param = (param & 0x0F) | 0x10; break;
171 		case 0x40:	param = (param & 0x0F) | 0x30; break;
172 		case 0x50:	param = (param & 0x0F) | 0x20; break;
173 		case 0x60:	param = (param & 0x0F) | 0xB0; break;
174 		case 0x70:	param = (param & 0x0F) | 0x40; break;
175 		case 0x90:	command = 'Q'; param &= 0x0F; break;
176 		case 0xA0:	if (param & 0x0F) { command = 'D'; param = (param << 4) | 0x0F; } else command=param=0; break;
177 		case 0xB0:	if (param & 0x0F) { command = 'D'; param |= 0xF0; } else command=param=0; break;
178 		}
179 		break;
180 	default:	command = param = 0;
181 	}
182 	command &= ~0x40;
183 	*pcmd = command;
184 	*pprm = param;
185 }
186 #endif // MODPLUG_NO_FILESAVE
187 
188 static DWORD boundInput(DWORD input, DWORD smin, DWORD smax)
189 {
190 	if (input > smax) input = smax;
191 	else if (input < smin) input = 0;
192 	return(input);
193 }
194 
195 
196 BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength)
197 //---------------------------------------------------------------
198 {
199 	UINT insnum,patnum,nins,npat;
200 	DWORD insfile[MAX_SAMPLES];
201 	WORD ptr[256];
202 	DWORD dwMemPos;
203 	BYTE insflags[MAX_SAMPLES], inspack[MAX_SAMPLES];
204 
205 	if ((!lpStream) || (dwMemLength <= sizeof(S3MFILEHEADER)+sizeof(S3MSAMPLESTRUCT)+64)) return FALSE;
206 	S3MFILEHEADER psfh = *(S3MFILEHEADER *)lpStream;
207 
208 	psfh.reserved1 = bswapLE16(psfh.reserved1);
209 	psfh.ordnum = bswapLE16(psfh.ordnum);
210 	psfh.insnum = bswapLE16(psfh.insnum);
211 	psfh.patnum = bswapLE16(psfh.patnum);
212 	psfh.flags = bswapLE16(psfh.flags);
213 	psfh.cwtv = bswapLE16(psfh.cwtv);
214 	psfh.version = bswapLE16(psfh.version);
215 	psfh.scrm = bswapLE32(psfh.scrm);
216 	psfh.special = bswapLE16(psfh.special);
217 
218 	if (psfh.scrm != 0x4D524353) return FALSE;
219 	dwMemPos = 0x60;
220 	m_nType = MOD_TYPE_S3M;
221 	memset(m_szNames,0,sizeof(m_szNames));
222 	memcpy(m_szNames[0], psfh.name, 28);
223 	// Speed
224 	m_nDefaultSpeed = psfh.speed;
225 	if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 6;
226 	if (m_nDefaultSpeed > 0x1F) m_nDefaultSpeed = 0x1F;
227 	// Tempo
228 	m_nDefaultTempo = psfh.tempo;
229 	if (m_nDefaultTempo < 40) m_nDefaultTempo = 40;
230 	if (m_nDefaultTempo > 240) m_nDefaultTempo = 240;
231 	// Global Volume
232 	m_nDefaultGlobalVolume = psfh.globalvol << 2;
233 	if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
234 	m_nSongPreAmp = psfh.mastervol & 0x7F;
235 	// Channels
236 	m_nChannels = 4;
237 	for (UINT ich=0; ich<32; ich++)
238 	{
239 		ChnSettings[ich].nPan = 128;
240 		ChnSettings[ich].nVolume = 64;
241 
242 		ChnSettings[ich].dwFlags = CHN_MUTE;
243 		if (psfh.channels[ich] != 0xFF)
244 		{
245 			m_nChannels = ich+1;
246 			UINT b = psfh.channels[ich] & 0x0F;
247 			ChnSettings[ich].nPan = (b & 8) ? 0xC0 : 0x40;
248 			ChnSettings[ich].dwFlags = 0;
249 		}
250 	}
251 	if (m_nChannels < 4) m_nChannels = 4;
252 	if ((psfh.cwtv < 0x1320) || (psfh.flags & 0x40)) m_dwSongFlags |= SONG_FASTVOLSLIDES;
253 	// Reading pattern order
254 	UINT iord = psfh.ordnum;
255 	if (iord<1) iord = 1;
256 	if (iord > MAX_ORDERS) iord = MAX_ORDERS;
257 	if (iord)
258 	{
259 		memcpy(Order, lpStream+dwMemPos, iord);
260 		dwMemPos += iord;
261 	}
262 	if ((iord & 1) && (lpStream[dwMemPos] == 0xFF)) dwMemPos++;
263 	// Reading file pointers
264 	insnum = nins = psfh.insnum;
265 	if (insnum >= MAX_SAMPLES) insnum = MAX_SAMPLES-1;
266 	m_nSamples = insnum;
267 	patnum = npat = psfh.patnum;
268 	if (patnum > MAX_PATTERNS) patnum = MAX_PATTERNS;
269 	memset(ptr, 0, sizeof(ptr));
270 
271 	// Ignore file if it has a corrupted header.
272 	if (nins+npat > 256) return FALSE;
273 
274 	if (nins+npat)
275 	{
276 		memcpy(ptr, lpStream+dwMemPos, 2*(nins+npat));
277 		dwMemPos += 2*(nins+npat);
278 		for (UINT j = 0; j < (nins+npat); ++j) {
279 		        ptr[j] = bswapLE16(ptr[j]);
280 		}
281 		if (psfh.panning_present == 252)
282 		{
283 			const BYTE *chnpan = lpStream+dwMemPos;
284 			for (UINT i=0; i<32; i++) if (chnpan[i] & 0x20)
285 			{
286 				ChnSettings[i].nPan = ((chnpan[i] & 0x0F) << 4) + 8;
287 			}
288 		}
289 	}
290 	if (!m_nChannels) return TRUE;
291 	// Reading instrument headers
292 	memset(insfile, 0, sizeof(insfile));
293 	for (UINT iSmp=1; iSmp<=insnum; iSmp++)
294 	{
295 		UINT nInd = ((DWORD)ptr[iSmp-1])*16;
296 		if ((!nInd) || (nInd + 0x50 > dwMemLength)) {
297 			// initialize basic variables.
298 			insflags[iSmp-1] = 0;
299 			inspack[iSmp-1] = 0;
300 			continue;
301 		}
302 		S3MSAMPLESTRUCT pSmp;
303 		memcpy(&pSmp, lpStream+nInd, 0x50);
304 		memcpy(Ins[iSmp].name, &pSmp.dosname, 12);
305 		insflags[iSmp-1] = pSmp.flags;
306 		inspack[iSmp-1] = pSmp.pack;
307 		memcpy(m_szNames[iSmp], pSmp.name, 28);
308 		m_szNames[iSmp][28] = 0;
309 		if ((pSmp.type==1) && (pSmp.scrs[2]=='R') && (pSmp.scrs[3]=='S'))
310 		{
311 			Ins[iSmp].nLength = boundInput(bswapLE32(pSmp.length), 4, MAX_SAMPLE_LENGTH);
312 			Ins[iSmp].nLoopStart = boundInput(bswapLE32(pSmp.loopbegin), 4, Ins[iSmp].nLength - 1);
313 			Ins[iSmp].nLoopEnd = boundInput(bswapLE32(pSmp.loopend), 4, Ins[iSmp].nLength);
314 			Ins[iSmp].nVolume = boundInput(pSmp.vol, 0, 64) << 2;
315 			Ins[iSmp].nGlobalVol = 64;
316 			if (pSmp.flags&1) Ins[iSmp].uFlags |= CHN_LOOP;
317 			UINT j = bswapLE32(pSmp.finetune);
318 			if (!j) j = 8363;
319 			if (j < 1024) j = 1024;
320 			Ins[iSmp].nC4Speed = j;
321 			insfile[iSmp] = (pSmp.hmem << 20) + (bswapLE16(pSmp.memseg) << 4);
322 			// offset is invalid - ignore this sample.
323 			if (insfile[iSmp] > dwMemLength) insfile[iSmp] = 0;
324 			else if (insfile[iSmp]) {
325 				// ignore duplicate samples.
326 				for (int z=iSmp-1; z>=0; z--)
327 					if (insfile[iSmp] == insfile[z])
328 						insfile[iSmp] = 0;
329 			}
330 			if ((Ins[iSmp].nLoopStart >= Ins[iSmp].nLoopEnd) || (Ins[iSmp].nLoopEnd - Ins[iSmp].nLoopStart < 8))
331 				Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0;
332 			Ins[iSmp].nPan = 0x80;
333 		}
334 	}
335 	// Reading patterns
336 	for (UINT iPat=0; iPat<patnum; iPat++)
337 	{
338 		UINT nInd = ((DWORD)ptr[nins+iPat]) << 4;
339 		if (nInd + 0x40 > dwMemLength) continue;
340 		WORD len = bswapLE16(*((WORD *)(lpStream+nInd)));
341 		nInd += 2;
342 		PatternSize[iPat] = 64;
343 		if ((!len) || (nInd + len > dwMemLength - 6)
344 		 || ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue;
345 		LPBYTE src = (LPBYTE)(lpStream+nInd);
346 		// Unpacking pattern
347 		MODCOMMAND *p = Patterns[iPat];
348 		UINT row = 0;
349 		UINT j = 0;
350 		while (j < len)
351 		{
352 			BYTE b = src[j++];
353 			if (!b)
354 			{
355 				if (++row >= 64) break;
356 			} else
357 			{
358 				UINT chn = b & 0x1F;
359 				if (chn < m_nChannels)
360 				{
361 					MODCOMMAND *m = &p[row*m_nChannels+chn];
362 					if (b & 0x20)
363 					{
364 						m->note = src[j++];
365 						if (m->note < 0xF0) m->note = (m->note & 0x0F) + 12*(m->note >> 4) + 13;
366 						else if (m->note == 0xFF) m->note = 0;
367 						m->instr = src[j++];
368 					}
369 					if (b & 0x40)
370 					{
371 						UINT vol = src[j++];
372 						if ((vol >= 128) && (vol <= 192))
373 						{
374 							vol -= 128;
375 							m->volcmd = VOLCMD_PANNING;
376 						} else
377 						{
378 							if (vol > 64) vol = 64;
379 							m->volcmd = VOLCMD_VOLUME;
380 						}
381 						m->vol = vol;
382 					}
383 					if (b & 0x80)
384 					{
385 						m->command = src[j++];
386 						m->param = src[j++];
387 						if (m->command) S3MConvert(m, FALSE);
388 					}
389 				} else
390 				{
391 					if (b & 0x20) j += 2;
392 					if (b & 0x40) j++;
393 					if (b & 0x80) j += 2;
394 				}
395 				if (j >= len) break;
396 			}
397 		}
398 	}
399 	// Reading samples
400 	for (UINT iRaw=1; iRaw<=insnum; iRaw++) if ((Ins[iRaw].nLength) && (insfile[iRaw]))
401 	{
402 		UINT flags = (psfh.version == 1) ? RS_PCM8S : RS_PCM8U;
403 		if (insflags[iRaw-1] & 4) flags += 5;
404 		if (insflags[iRaw-1] & 2) flags |= RSF_STEREO;
405 		if (inspack[iRaw-1] == 4) flags = RS_ADPCM4;
406 		dwMemPos = insfile[iRaw];
407 		if (dwMemPos < dwMemLength)
408 			ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
409 	}
410 	m_nMinPeriod = 64;
411 	m_nMaxPeriod = 32767;
412 	if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS;
413 	return TRUE;
414 }
415 
416 
417 #ifndef MODPLUG_NO_FILESAVE
418 
419 #ifdef _MSC_VER
420 #pragma warning(disable:4100)
421 #endif
422 
423 static BYTE S3MFiller[16] =
424 {
425 	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
426 	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
427 };
428 
429 
430 BOOL CSoundFile::SaveS3M(LPCSTR lpszFileName, UINT nPacking)
431 //----------------------------------------------------------
432 {
433 	FILE *f;
434 	BYTE header[0x60];
435 	UINT nbo,nbi,nbp,i;
436 	WORD patptr[128];
437 	WORD insptr[128];
438 	BYTE buffer[5*1024];
439 	S3MSAMPLESTRUCT insex[128];
440 
441 	if ((!m_nChannels) || (!lpszFileName)) return FALSE;
442 	if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
443 	// Writing S3M header
444 	memset(header, 0, sizeof(header));
445 	memset(insex, 0, sizeof(insex));
446 	memcpy(header, m_szNames[0], 0x1C);
447 	header[0x1B] = 0;
448 	header[0x1C] = 0x1A;
449 	header[0x1D] = 0x10;
450 	nbo = (GetNumPatterns() + 15) & 0xF0;
451 	if (!nbo) nbo = 16;
452 	header[0x20] = nbo & 0xFF;
453 	header[0x21] = nbo >> 8;
454 	nbi = m_nInstruments;
455 	if (!nbi) nbi = m_nSamples;
456 	if (nbi > 99) nbi = 99;
457 	header[0x22] = nbi & 0xFF;
458 	header[0x23] = nbi >> 8;
459 	nbp = 0;
460 	for (i=0; Patterns[i]; i++) { nbp = i+1; if (nbp >= MAX_PATTERNS) break; }
461 	for (i=0; i<MAX_ORDERS; i++) if ((Order[i] < MAX_PATTERNS) && (Order[i] >= nbp)) nbp = Order[i] + 1;
462 	header[0x24] = nbp & 0xFF;
463 	header[0x25] = nbp >> 8;
464 	if (m_dwSongFlags & SONG_FASTVOLSLIDES) header[0x26] |= 0x40;
465 	if ((m_nMaxPeriod < 20000) || (m_dwSongFlags & SONG_AMIGALIMITS)) header[0x26] |= 0x10;
466 	header[0x28] = 0x20;
467 	header[0x29] = 0x13;
468 	header[0x2A] = 0x02; // Version = 1 => Signed samples
469 	header[0x2B] = 0x00;
470 	header[0x2C] = 'S';
471 	header[0x2D] = 'C';
472 	header[0x2E] = 'R';
473 	header[0x2F] = 'M';
474 	header[0x30] = m_nDefaultGlobalVolume >> 2;
475 	header[0x31] = m_nDefaultSpeed;
476 	header[0x32] = m_nDefaultTempo;
477 	header[0x33] = ((m_nSongPreAmp < 0x20) ? 0x20 : m_nSongPreAmp) | 0x80;	// Stereo
478 	header[0x35] = 0xFC;
479 	for (i=0; i<32; i++)
480 	{
481 		if (i < m_nChannels)
482 		{
483 			UINT tmp = (i & 0x0F) >> 1;
484 			header[0x40+i] = (i & 0x10) | ((i & 1) ? 8+tmp : tmp);
485 		} else header[0x40+i] = 0xFF;
486 	}
487 	fwrite(header, 0x60, 1, f);
488 	fwrite(Order, nbo, 1, f);
489 	memset(patptr, 0, sizeof(patptr));
490 	memset(insptr, 0, sizeof(insptr));
491 	UINT ofs0 = 0x60 + nbo;
492 	UINT ofs1 = ((0x60 + nbo + nbi*2 + nbp*2 + 15) & 0xFFF0) + 0x20;
493 	UINT ofs = ofs1;
494 
495 	for (i=0; i<nbi; i++) insptr[i] = (WORD)((ofs + i*0x50) / 16);
496 	for (i=0; i<nbp; i++) patptr[i] = (WORD)((ofs + nbi*0x50) / 16);
497 	fwrite(insptr, nbi, 2, f);
498 	fwrite(patptr, nbp, 2, f);
499 	if (header[0x35] == 0xFC)
500 	{
501 		BYTE chnpan[32];
502 		for (i=0; i<32; i++)
503 		{
504 			chnpan[i] = 0x20 | (ChnSettings[i].nPan >> 4);
505 		}
506 		fwrite(chnpan, 0x20, 1, f);
507 	}
508 	if ((nbi*2+nbp*2) & 0x0F)
509 	{
510 		fwrite(S3MFiller, 0x10 - ((nbi*2+nbp*2) & 0x0F), 1, f);
511 	}
512 	ofs1 = ftell(f);
513 	fwrite(insex, nbi, 0x50, f);
514 	// Packing patterns
515 	ofs += nbi*0x50;
516 	for (i=0; i<nbp; i++)
517 	{
518 		WORD len = 64;
519 		memset(buffer, 0, sizeof(buffer));
520 		patptr[i] = ofs / 16;
521 		if (Patterns[i])
522 		{
523 			len = 2;
524 			MODCOMMAND *p = Patterns[i];
525 			for (int row=0; row<64; row++) if (row < PatternSize[i])
526 			{
527 				for (UINT j=0; j<m_nChannels; j++)
528 				{
529 					UINT b = j;
530 					MODCOMMAND *m = &p[row*m_nChannels+j];
531 					UINT note = m->note;
532 					UINT volcmd = m->volcmd;
533 					UINT vol = m->vol;
534 					UINT command = m->command;
535 					UINT param = m->param;
536 
537 					if ((note) || (m->instr)) b |= 0x20;
538 					if (!note) note = 0xFF; else
539 					if (note >= 0xFE) note = 0xFE; else
540 					if (note < 13) note = 0; else note -= 13;
541 					if (note < 0xFE) note = (note % 12) + ((note / 12) << 4);
542 					if (command == CMD_VOLUME)
543 					{
544 						command = 0;
545 						if (param > 64) param = 64;
546 						volcmd = VOLCMD_VOLUME;
547 						vol = param;
548 					}
549 					if (volcmd == VOLCMD_VOLUME) b |= 0x40; else
550 					if (volcmd == VOLCMD_PANNING) { vol |= 0x80; b |= 0x40; }
551 					if (command)
552 					{
553 						S3MSaveConvert(&command, &param, FALSE);
554 						if (command) b |= 0x80;
555 					}
556 					if (b & 0xE0)
557 					{
558 						buffer[len++] = b;
559 						if (b & 0x20)
560 						{
561 							buffer[len++] = note;
562 							buffer[len++] = m->instr;
563 						}
564 						if (b & 0x40)
565 						{
566 							buffer[len++] = vol;
567 						}
568 						if (b & 0x80)
569 						{
570 							buffer[len++] = command;
571 							buffer[len++] = param;
572 						}
573 						if (len > sizeof(buffer) - 20) break;
574 					}
575 				}
576 				buffer[len++] = 0;
577 				if (len > sizeof(buffer) - 20) break;
578 			}
579 		}
580 		buffer[0] = (len - 2) & 0xFF;
581 		buffer[1] = (len - 2) >> 8;
582 		len = (len+15) & (~0x0F);
583 		fwrite(buffer, len, 1, f);
584 		ofs += len;
585 	}
586 	// Writing samples
587 	for (i=1; i<=nbi; i++)
588 	{
589 		MODINSTRUMENT *pins = &Ins[i];
590 		if (m_nInstruments)
591 		{
592 			pins = Ins;
593 			if (Headers[i])
594 			{
595 				for (UINT j=0; j<128; j++)
596 				{
597 					UINT n = Headers[i]->Keyboard[j];
598 					if ((n) && (n < MAX_INSTRUMENTS))
599 					{
600 						pins = &Ins[n];
601 						break;
602 					}
603 				}
604 			}
605 		}
606 		memcpy(insex[i-1].dosname, pins->name, 12);
607 		memcpy(insex[i-1].name, m_szNames[i], 28);
608 		memcpy(insex[i-1].scrs, "SCRS", 4);
609 		insex[i-1].hmem = (BYTE)((DWORD)ofs >> 20);
610 		insex[i-1].memseg = (WORD)((DWORD)ofs >> 4);
611 		if (pins->pSample)
612 		{
613 			insex[i-1].type = 1;
614 			insex[i-1].length = pins->nLength;
615 			insex[i-1].loopbegin = pins->nLoopStart;
616 			insex[i-1].loopend = pins->nLoopEnd;
617 			insex[i-1].vol = pins->nVolume / 4;
618 			insex[i-1].flags = (pins->uFlags & CHN_LOOP) ? 1 : 0;
619 			if (pins->nC4Speed)
620 				insex[i-1].finetune = pins->nC4Speed;
621 			else
622 				insex[i-1].finetune = TransposeToFrequency(pins->RelativeTone, pins->nFineTune);
623 			UINT flags = RS_PCM8U;
624 #ifndef NO_PACKING
625 			if (nPacking)
626 			{
627 				if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
628 				 && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking)))
629 				{
630 					insex[i-1].pack = 4;
631 					flags = RS_ADPCM4;
632 				}
633 			} else
634 #endif // NO_PACKING
635 			{
636 				if (pins->uFlags & CHN_16BIT)
637 				{
638 					insex[i-1].flags |= 4;
639 					flags = RS_PCM16U;
640 				}
641 				if (pins->uFlags & CHN_STEREO)
642 				{
643 					insex[i-1].flags |= 2;
644 					flags = (pins->uFlags & CHN_16BIT) ? RS_STPCM16U : RS_STPCM8U;
645 				}
646 			}
647 			DWORD len = WriteSample(f, pins, flags);
648 			if (len & 0x0F)
649 			{
650 				fwrite(S3MFiller, 0x10 - (len & 0x0F), 1, f);
651 			}
652 			ofs += (len + 15) & (~0x0F);
653 		} else
654 		{
655 			insex[i-1].length = 0;
656 		}
657 	}
658 	// Updating parapointers
659 	fseek(f, ofs0, SEEK_SET);
660 	fwrite(insptr, nbi, 2, f);
661 	fwrite(patptr, nbp, 2, f);
662 	fseek(f, ofs1, SEEK_SET);
663 	fwrite(insex, 0x50, nbi, f);
664 	fclose(f);
665 	return TRUE;
666 }
667 
668 #ifdef _MSC_VER
669 #pragma warning(default:4100)
670 #endif
671 
672 #endif // MODPLUG_NO_FILESAVE
673