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