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.cpp | 653 |
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 | |||
16 | extern WORD S3MFineTuneTable[16]; | ||
17 | |||
18 | ////////////////////////////////////////////////////// | ||
19 | // ScreamTracker S3M file support | ||
20 | |||
21 | typedef 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 | |||
44 | typedef 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 | |||
69 | void 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 | |||
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 | |||
187 | |||
188 | BOOL 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 | |||
406 | static 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 | |||
413 | BOOL 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, ¶m, 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 | |||