Diffstat (limited to 'core/multimedia/opieplayer/modplug/load_mod.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/multimedia/opieplayer/modplug/load_mod.cpp | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/modplug/load_mod.cpp b/core/multimedia/opieplayer/modplug/load_mod.cpp new file mode 100644 index 0000000..3ca8e5b --- a/dev/null +++ b/core/multimedia/opieplayer/modplug/load_mod.cpp | |||
@@ -0,0 +1,501 @@ | |||
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 ProTrackerPeriodTable[6*12]; | ||
17 | |||
18 | ////////////////////////////////////////////////////////// | ||
19 | // ProTracker / NoiseTracker MOD/NST file support | ||
20 | |||
21 | void CSoundFile::ConvertModCommand(MODCOMMAND *m) const | ||
22 | //----------------------------------------------------- | ||
23 | { | ||
24 | UINT command = m->command, param = m->param; | ||
25 | |||
26 | switch(command) | ||
27 | { | ||
28 | case 0x00:if (param) command = CMD_ARPEGGIO; break; | ||
29 | case 0x01:command = CMD_PORTAMENTOUP; break; | ||
30 | case 0x02:command = CMD_PORTAMENTODOWN; break; | ||
31 | case 0x03:command = CMD_TONEPORTAMENTO; break; | ||
32 | case 0x04:command = CMD_VIBRATO; break; | ||
33 | case 0x05:command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break; | ||
34 | case 0x06:command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break; | ||
35 | case 0x07:command = CMD_TREMOLO; break; | ||
36 | case 0x08:command = CMD_PANNING8; break; | ||
37 | case 0x09:command = CMD_OFFSET; break; | ||
38 | case 0x0A:command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break; | ||
39 | case 0x0B:command = CMD_POSITIONJUMP; break; | ||
40 | case 0x0C:command = CMD_VOLUME; break; | ||
41 | case 0x0D:command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break; | ||
42 | case 0x0E:command = CMD_MODCMDEX; break; | ||
43 | case 0x0F:command = (param <= (UINT)((m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? 0x1F : 0x20)) ? CMD_SPEED : CMD_TEMPO; | ||
44 | if ((param == 0xFF) && (m_nSamples == 15)) command = 0; break; | ||
45 | // Extension for XM extended effects | ||
46 | case 'G' - 55:command = CMD_GLOBALVOLUME; break; | ||
47 | case 'H' - 55:command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break; | ||
48 | case 'K' - 55:command = CMD_KEYOFF; break; | ||
49 | case 'L' - 55:command = CMD_SETENVPOSITION; break; | ||
50 | case 'M' - 55:command = CMD_CHANNELVOLUME; break; | ||
51 | case 'N' - 55:command = CMD_CHANNELVOLSLIDE; break; | ||
52 | case 'P' - 55:command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break; | ||
53 | case 'R' - 55:command = CMD_RETRIG; break; | ||
54 | case 'T' - 55:command = CMD_TREMOR; break; | ||
55 | case 'X' - 55: command = CMD_XFINEPORTAUPDOWN;break; | ||
56 | case 'Y' - 55:command = CMD_PANBRELLO; break; | ||
57 | case 'Z' - 55: command = CMD_MIDI;break; | ||
58 | default:command = 0; | ||
59 | } | ||
60 | m->command = command; | ||
61 | m->param = param; | ||
62 | } | ||
63 | |||
64 | |||
65 | WORD CSoundFile::ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const | ||
66 | //------------------------------------------------------------------ | ||
67 | { | ||
68 | UINT command = m->command & 0x3F, param = m->param; | ||
69 | |||
70 | switch(command) | ||
71 | { | ||
72 | case 0: command = param = 0; break; | ||
73 | case CMD_ARPEGGIO: command = 0; break; | ||
74 | case CMD_PORTAMENTOUP: | ||
75 | if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) | ||
76 | { | ||
77 | if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x10; break; } | ||
78 | else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x10; break; } | ||
79 | } | ||
80 | command = 0x01; | ||
81 | break; | ||
82 | case CMD_PORTAMENTODOWN: | ||
83 | if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) | ||
84 | { | ||
85 | if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x20; break; } | ||
86 | else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x20; break; } | ||
87 | } | ||
88 | command = 0x02; | ||
89 | break; | ||
90 | case CMD_TONEPORTAMENTO:command = 0x03; break; | ||
91 | case CMD_VIBRATO: command = 0x04; break; | ||
92 | case CMD_TONEPORTAVOL: command = 0x05; break; | ||
93 | case CMD_VIBRATOVOL: command = 0x06; break; | ||
94 | case CMD_TREMOLO: command = 0x07; break; | ||
95 | case CMD_PANNING8: | ||
96 | command = 0x08; | ||
97 | if (bXM) | ||
98 | { | ||
99 | if ((m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM) && (param <= 0x80)) | ||
100 | { | ||
101 | param <<= 1; | ||
102 | if (param > 255) param = 255; | ||
103 | } | ||
104 | } else | ||
105 | { | ||
106 | if ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)) param >>= 1; | ||
107 | } | ||
108 | break; | ||
109 | case CMD_OFFSET: command = 0x09; break; | ||
110 | case CMD_VOLUMESLIDE: command = 0x0A; break; | ||
111 | case CMD_POSITIONJUMP: command = 0x0B; break; | ||
112 | case CMD_VOLUME: command = 0x0C; break; | ||
113 | case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break; | ||
114 | case CMD_MODCMDEX: command = 0x0E; break; | ||
115 | case CMD_SPEED: command = 0x0F; if (param > 0x20) param = 0x20; break; | ||
116 | case CMD_TEMPO: if (param > 0x20) { command = 0x0F; break; } | ||
117 | case CMD_GLOBALVOLUME: command = 'G' - 55; break; | ||
118 | case CMD_GLOBALVOLSLIDE:command = 'H' - 55; break; | ||
119 | case CMD_KEYOFF: command = 'K' - 55; break; | ||
120 | case CMD_SETENVPOSITION:command = 'L' - 55; break; | ||
121 | case CMD_CHANNELVOLUME: command = 'M' - 55; break; | ||
122 | case CMD_CHANNELVOLSLIDE:command = 'N' - 55; break; | ||
123 | case CMD_PANNINGSLIDE: command = 'P' - 55; break; | ||
124 | case CMD_RETRIG: command = 'R' - 55; break; | ||
125 | case CMD_TREMOR: command = 'T' - 55; break; | ||
126 | case CMD_XFINEPORTAUPDOWN:command = 'X' - 55; break; | ||
127 | case CMD_PANBRELLO: command = 'Y' - 55; break; | ||
128 | case CMD_MIDI: command = 'Z' - 55; break; | ||
129 | case CMD_S3MCMDEX: | ||
130 | switch(param & 0xF0) | ||
131 | { | ||
132 | case 0x10:command = 0x0E; param = (param & 0x0F) | 0x30; break; | ||
133 | case 0x20:command = 0x0E; param = (param & 0x0F) | 0x50; break; | ||
134 | case 0x30:command = 0x0E; param = (param & 0x0F) | 0x40; break; | ||
135 | case 0x40:command = 0x0E; param = (param & 0x0F) | 0x70; break; | ||
136 | case 0x90:command = 'X' - 55; break; | ||
137 | case 0xB0:command = 0x0E; param = (param & 0x0F) | 0x60; break; | ||
138 | case 0xA0: | ||
139 | case 0x50: | ||
140 | case 0x70: | ||
141 | case 0x60:command = param = 0; break; | ||
142 | default:command = 0x0E; break; | ||
143 | } | ||
144 | break; | ||
145 | default: command = param = 0; | ||
146 | } | ||
147 | return (WORD)((command << 8) | (param)); | ||
148 | } | ||
149 | |||
150 | |||
151 | #pragma pack(1) | ||
152 | |||
153 | typedef struct _MODSAMPLE | ||
154 | { | ||
155 | CHAR name[22]; | ||
156 | WORD length; | ||
157 | BYTE finetune; | ||
158 | BYTE volume; | ||
159 | WORD loopstart; | ||
160 | WORD looplen; | ||
161 | } Q_PACKED MODSAMPLE, *PMODSAMPLE; | ||
162 | |||
163 | typedef struct _MODMAGIC | ||
164 | { | ||
165 | BYTE nOrders; | ||
166 | BYTE nRestartPos; | ||
167 | BYTE Orders[128]; | ||
168 | char Magic[4]; // changed from CHAR | ||
169 | } Q_PACKED MODMAGIC, *PMODMAGIC; | ||
170 | |||
171 | #pragma pack() | ||
172 | |||
173 | BOOL IsMagic(LPCSTR s1, LPCSTR s2) | ||
174 | { | ||
175 | return ((*(DWORD *)s1) == (*(DWORD *)s2)) ? TRUE : FALSE; | ||
176 | } | ||
177 | |||
178 | |||
179 | BOOL CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength) | ||
180 | //--------------------------------------------------------------- | ||
181 | { | ||
182 | char s[1024]; // changed from CHAR | ||
183 | DWORD dwMemPos, dwTotalSampleLen; | ||
184 | PMODMAGIC pMagic; | ||
185 | UINT nErr; | ||
186 | |||
187 | if ((!lpStream) || (dwMemLength < 0x600)) return FALSE; | ||
188 | dwMemPos = 20; | ||
189 | m_nSamples = 31; | ||
190 | m_nChannels = 4; | ||
191 | pMagic = (PMODMAGIC)(lpStream+dwMemPos+sizeof(MODSAMPLE)*31); | ||
192 | // Check Mod Magic | ||
193 | memcpy(s, pMagic->Magic, 4); | ||
194 | if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!")) | ||
195 | || (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T."))) m_nChannels = 4; else | ||
196 | if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8; else | ||
197 | if ((s[0]=='F') && (s[1]=='L') && (s[2]=='T') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else | ||
198 | if ((s[0]>='4') && (s[0]<='9') && (s[1]=='C') && (s[2]=='H') && (s[3]=='N')) m_nChannels = s[0] - '0'; else | ||
199 | if ((s[0]=='1') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 10; else | ||
200 | if ((s[0]=='2') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 20; else | ||
201 | if ((s[0]=='3') && (s[1]>='0') && (s[1]<='2') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 30; else | ||
202 | if ((s[0]=='T') && (s[1]=='D') && (s[2]=='Z') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else | ||
203 | if (IsMagic(s,"16CN")) m_nChannels = 16; else | ||
204 | if (IsMagic(s,"32CN")) m_nChannels = 32; else m_nSamples = 15; | ||
205 | // Load Samples | ||
206 | nErr = 0; | ||
207 | dwTotalSampleLen = 0; | ||
208 | for(UINT i=1; i<=m_nSamples; i++) | ||
209 | { | ||
210 | PMODSAMPLE pms = (PMODSAMPLE)(lpStream+dwMemPos); | ||
211 | MODINSTRUMENT *psmp = &Ins[i]; | ||
212 | UINT loopstart, looplen; | ||
213 | |||
214 | memcpy(m_szNames[i], pms->name, 22); | ||
215 | m_szNames[i][22] = 0; | ||
216 | psmp->uFlags = 0; | ||
217 | psmp->nLength = bswapBE16(pms->length)*2; | ||
218 | dwTotalSampleLen += psmp->nLength; | ||
219 | psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F); | ||
220 | psmp->nVolume = 4*pms->volume; | ||
221 | if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; } | ||
222 | psmp->nGlobalVol = 64; | ||
223 | psmp->nPan = 128; | ||
224 | loopstart = bswapBE16(pms->loopstart)*2; | ||
225 | looplen = bswapBE16(pms->looplen)*2; | ||
226 | // Fix loops | ||
227 | if ((looplen > 2) && (loopstart+looplen > psmp->nLength) | ||
228 | && (loopstart/2+looplen <= psmp->nLength)) | ||
229 | { | ||
230 | loopstart /= 2; | ||
231 | } | ||
232 | psmp->nLoopStart = loopstart; | ||
233 | psmp->nLoopEnd = loopstart + looplen; | ||
234 | if (psmp->nLength < 4) psmp->nLength = 0; | ||
235 | if (psmp->nLength) | ||
236 | { | ||
237 | UINT derr = 0; | ||
238 | if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopStart = psmp->nLength-1; derr|=1; } | ||
239 | if (psmp->nLoopEnd > psmp->nLength) { psmp->nLoopEnd = psmp->nLength; derr |= 1; } | ||
240 | if (psmp->nLoopStart > psmp->nLoopEnd) derr |= 1; | ||
241 | if ((psmp->nLoopStart > psmp->nLoopEnd) || (psmp->nLoopEnd <= 8) | ||
242 | || (psmp->nLoopEnd - psmp->nLoopStart <= 4)) | ||
243 | { | ||
244 | psmp->nLoopStart = 0; | ||
245 | psmp->nLoopEnd = 0; | ||
246 | } | ||
247 | if (psmp->nLoopEnd > psmp->nLoopStart) | ||
248 | { | ||
249 | psmp->uFlags |= CHN_LOOP; | ||
250 | } | ||
251 | } | ||
252 | dwMemPos += sizeof(MODSAMPLE); | ||
253 | } | ||
254 | if ((m_nSamples == 15) && (dwTotalSampleLen > dwMemLength * 4)) return FALSE; | ||
255 | pMagic = (PMODMAGIC)(lpStream+dwMemPos); | ||
256 | dwMemPos += sizeof(MODMAGIC); | ||
257 | if (m_nSamples == 15) dwMemPos -= 4; | ||
258 | memset(Order, 0,sizeof(Order)); | ||
259 | memcpy(Order, pMagic->Orders, 128); | ||
260 | |||
261 | UINT nbp, nbpbuggy, nbpbuggy2, norders; | ||
262 | |||
263 | norders = pMagic->nOrders; | ||
264 | if ((!norders) || (norders > 0x80)) | ||
265 | { | ||
266 | norders = 0x80; | ||
267 | while ((norders > 1) && (!Order[norders-1])) norders--; | ||
268 | } | ||
269 | nbpbuggy = 0; | ||
270 | nbpbuggy2 = 0; | ||
271 | nbp = 0; | ||
272 | for (UINT iord=0; iord<128; iord++) | ||
273 | { | ||
274 | UINT i = Order[iord]; | ||
275 | if ((i < 0x80) && (nbp <= i)) | ||
276 | { | ||
277 | nbp = i+1; | ||
278 | if (iord<norders) nbpbuggy = nbp; | ||
279 | } | ||
280 | if (i >= nbpbuggy2) nbpbuggy2 = i+1; | ||
281 | } | ||
282 | for (UINT iend=norders; iend<MAX_ORDERS; iend++) Order[iend] = 0xFF; | ||
283 | norders--; | ||
284 | m_nRestartPos = pMagic->nRestartPos; | ||
285 | if (m_nRestartPos >= 0x78) m_nRestartPos = 0; | ||
286 | if (m_nRestartPos + 1 >= (UINT)norders) m_nRestartPos = 0; | ||
287 | if (!nbp) return FALSE; | ||
288 | DWORD dwWowTest = dwTotalSampleLen+dwMemPos; | ||
289 | if ((IsMagic(pMagic->Magic, "M.K.")) && (dwWowTest + nbp*8*256 == dwMemLength)) m_nChannels = 8; | ||
290 | if ((nbp != nbpbuggy) && (dwWowTest + nbp*m_nChannels*256 != dwMemLength)) | ||
291 | { | ||
292 | if (dwWowTest + nbpbuggy*m_nChannels*256 == dwMemLength) nbp = nbpbuggy; | ||
293 | else nErr += 8; | ||
294 | } else | ||
295 | if ((nbpbuggy2 > nbp) && (dwWowTest + nbpbuggy2*m_nChannels*256 == dwMemLength)) | ||
296 | { | ||
297 | nbp = nbpbuggy2; | ||
298 | } | ||
299 | if ((dwWowTest < 0x600) || (dwWowTest > dwMemLength)) nErr += 8; | ||
300 | if ((m_nSamples == 15) && (nErr >= 16)) return FALSE; | ||
301 | // Default settings | ||
302 | m_nType = MOD_TYPE_MOD; | ||
303 | m_nDefaultSpeed = 6; | ||
304 | m_nDefaultTempo = 125; | ||
305 | m_nMinPeriod = 14 << 2; | ||
306 | m_nMaxPeriod = 3424 << 2; | ||
307 | memcpy(m_szNames, lpStream, 20); | ||
308 | // Setting channels pan | ||
309 | for (UINT ich=0; ich<m_nChannels; ich++) | ||
310 | { | ||
311 | ChnSettings[ich].nVolume = 64; | ||
312 | if (gdwSoundSetup & SNDMIX_MAXDEFAULTPAN) | ||
313 | ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 256 : 0; | ||
314 | else | ||
315 | ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 0xC0 : 0x40; | ||
316 | } | ||
317 | // Reading channels | ||
318 | for (UINT ipat=0; ipat<nbp; ipat++) | ||
319 | { | ||
320 | if (ipat < MAX_PATTERNS) | ||
321 | { | ||
322 | if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break; | ||
323 | PatternSize[ipat] = 64; | ||
324 | if (dwMemPos + m_nChannels*256 >= dwMemLength) break; | ||
325 | MODCOMMAND *m = Patterns[ipat]; | ||
326 | LPCBYTE p = lpStream + dwMemPos; | ||
327 | for (UINT j=m_nChannels*64; j; m++,p+=4,j--) | ||
328 | { | ||
329 | BYTE A0=p[0], A1=p[1], A2=p[2], A3=p[3]; | ||
330 | UINT n = ((((UINT)A0 & 0x0F) << 8) | (A1)); | ||
331 | if ((n) && (n != 0xFFF)) m->note = GetNoteFromPeriod(n << 2); | ||
332 | m->instr = ((UINT)A2 >> 4) | (A0 & 0x10); | ||
333 | m->command = A2 & 0x0F; | ||
334 | m->param = A3; | ||
335 | if ((m->command) || (m->param)) ConvertModCommand(m); | ||
336 | } | ||
337 | } | ||
338 | dwMemPos += m_nChannels*256; | ||
339 | } | ||
340 | // Reading instruments | ||
341 | DWORD dwErrCheck = 0; | ||
342 | for (UINT ismp=1; ismp<=m_nSamples; ismp++) if (Ins[ismp].nLength) | ||
343 | { | ||
344 | LPSTR p = (LPSTR)(lpStream+dwMemPos); | ||
345 | UINT flags = 0; | ||
346 | if (dwMemPos + 5 >= dwMemLength) break; | ||
347 | if (!strnicmp(p, "ADPCM", 5)) | ||
348 | { | ||
349 | flags = 3; | ||
350 | p += 5; | ||
351 | dwMemPos += 5; | ||
352 | } | ||
353 | DWORD dwSize = ReadSample(&Ins[ismp], flags, p, dwMemLength - dwMemPos); | ||
354 | if (dwSize) | ||
355 | { | ||
356 | dwMemPos += dwSize; | ||
357 | dwErrCheck++; | ||
358 | } | ||
359 | } | ||
360 | #ifdef MODPLUG_TRACKER | ||
361 | return TRUE; | ||
362 | #else | ||
363 | return (dwErrCheck) ? TRUE : FALSE; | ||
364 | #endif | ||
365 | } | ||
366 | |||
367 | |||
368 | #ifndef MODPLUG_NO_FILESAVE | ||
369 | #pragma warning(disable:4100) | ||
370 | |||
371 | BOOL CSoundFile::SaveMod(LPCSTR lpszFileName, UINT nPacking) | ||
372 | //---------------------------------------------------------- | ||
373 | { | ||
374 | BYTE insmap[32]; | ||
375 | UINT inslen[32]; | ||
376 | BYTE bTab[32]; | ||
377 | BYTE ord[128]; | ||
378 | FILE *f; | ||
379 | |||
380 | if ((!m_nChannels) || (!lpszFileName)) return FALSE; | ||
381 | if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; | ||
382 | memset(ord, 0, sizeof(ord)); | ||
383 | memset(inslen, 0, sizeof(inslen)); | ||
384 | if (m_nInstruments) | ||
385 | { | ||
386 | memset(insmap, 0, sizeof(insmap)); | ||
387 | for (UINT i=1; i<32; i++) if (Headers[i]) | ||
388 | { | ||
389 | for (UINT j=0; j<128; j++) if (Headers[i]->Keyboard[j]) | ||
390 | { | ||
391 | insmap[i] = Headers[i]->Keyboard[j]; | ||
392 | break; | ||
393 | } | ||
394 | } | ||
395 | } else | ||
396 | { | ||
397 | for (UINT i=0; i<32; i++) insmap[i] = (BYTE)i; | ||
398 | } | ||
399 | // Writing song name | ||
400 | fwrite(m_szNames, 20, 1, f); | ||
401 | // Writing instrument definition | ||
402 | for (UINT iins=1; iins<=31; iins++) | ||
403 | { | ||
404 | MODINSTRUMENT *pins = &Ins[insmap[iins]]; | ||
405 | memcpy(bTab, m_szNames[iins],22); | ||
406 | inslen[iins] = pins->nLength; | ||
407 | if (inslen[iins] > 0x1fff0) inslen[iins] = 0x1fff0; | ||
408 | bTab[22] = inslen[iins] >> 9; | ||
409 | bTab[23] = inslen[iins] >> 1; | ||
410 | if (pins->RelativeTone < 0) bTab[24] = 0x08; else | ||
411 | if (pins->RelativeTone > 0) bTab[24] = 0x07; else | ||
412 | bTab[24] = (BYTE)XM2MODFineTune(pins->nFineTune); | ||
413 | bTab[25] = pins->nVolume >> 2; | ||
414 | bTab[26] = pins->nLoopStart >> 9; | ||
415 | bTab[27] = pins->nLoopStart >> 1; | ||
416 | bTab[28] = (pins->nLoopEnd - pins->nLoopStart) >> 9; | ||
417 | bTab[29] = (pins->nLoopEnd - pins->nLoopStart) >> 1; | ||
418 | fwrite(bTab, 30, 1, f); | ||
419 | } | ||
420 | // Writing number of patterns | ||
421 | UINT nbp=0, norders=128; | ||
422 | for (UINT iord=0; iord<128; iord++) | ||
423 | { | ||
424 | if (Order[iord] == 0xFF) | ||
425 | { | ||
426 | norders = iord; | ||
427 | break; | ||
428 | } | ||
429 | if ((Order[iord] < 0x80) && (nbp<=Order[iord])) nbp = Order[iord]+1; | ||
430 | } | ||
431 | bTab[0] = norders; | ||
432 | bTab[1] = m_nRestartPos; | ||
433 | fwrite(bTab, 2, 1, f); | ||
434 | // Writing pattern list | ||
435 | if (norders) memcpy(ord, Order, norders); | ||
436 | fwrite(ord, 128, 1, f); | ||
437 | // Writing signature | ||
438 | if (m_nChannels == 4) | ||
439 | lstrcpy((LPSTR)&bTab, "M.K."); | ||
440 | else | ||
441 | wsprintf((LPSTR)&bTab, "%luCHN", m_nChannels); | ||
442 | fwrite(bTab, 4, 1, f); | ||
443 | // Writing patterns | ||
444 | for (UINT ipat=0; ipat<nbp; ipat++) if (Patterns[ipat]) | ||
445 | { | ||
446 | BYTE s[64*4]; | ||
447 | MODCOMMAND *m = Patterns[ipat]; | ||
448 | for (UINT i=0; i<64; i++) if (i < PatternSize[ipat]) | ||
449 | { | ||
450 | LPBYTE p=s; | ||
451 | for (UINT c=0; c<m_nChannels; c++,p+=4,m++) | ||
452 | { | ||
453 | UINT param = ModSaveCommand(m, FALSE); | ||
454 | UINT command = param >> 8; | ||
455 | param &= 0xFF; | ||
456 | if (command > 0x0F) command = param = 0; | ||
457 | if ((m->vol >= 0x10) && (m->vol <= 0x50) && (!command) && (!param)) { command = 0x0C; param = m->vol - 0x10; } | ||
458 | UINT period = m->note; | ||
459 | if (period) | ||
460 | { | ||
461 | if (period < 37) period = 37; | ||
462 | period -= 37; | ||
463 | if (period >= 6*12) period = 6*12-1; | ||
464 | period = ProTrackerPeriodTable[period]; | ||
465 | } | ||
466 | UINT instr = (m->instr > 31) ? 0 : m->instr; | ||
467 | p[0] = ((period >> 8) & 0x0F) | (instr & 0x10); | ||
468 | p[1] = period & 0xFF; | ||
469 | p[2] = ((instr & 0x0F) << 4) | (command & 0x0F); | ||
470 | p[3] = param; | ||
471 | } | ||
472 | fwrite(s, m_nChannels, 4, f); | ||
473 | } else | ||
474 | { | ||
475 | memset(s, 0, m_nChannels*4); | ||
476 | fwrite(s, m_nChannels, 4, f); | ||
477 | } | ||
478 | } | ||
479 | // Writing instruments | ||
480 | for (UINT ismpd=1; ismpd<=31; ismpd++) if (inslen[ismpd]) | ||
481 | { | ||
482 | MODINSTRUMENT *pins = &Ins[insmap[ismpd]]; | ||
483 | UINT flags = RS_PCM8S; | ||
484 | #ifndef NO_PACKING | ||
485 | if (!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) | ||
486 | { | ||
487 | if ((nPacking) && (CanPackSample(pins->pSample, inslen[ismpd], nPacking))) | ||
488 | { | ||
489 | fwrite("ADPCM", 1, 5, f); | ||
490 | flags = RS_ADPCM4; | ||
491 | } | ||
492 | } | ||
493 | #endif | ||
494 | WriteSample(f, pins, flags, inslen[ismpd]); | ||
495 | } | ||
496 | fclose(f); | ||
497 | return TRUE; | ||
498 | } | ||
499 | |||
500 | #pragma warning(default:4100) | ||
501 | #endif // MODPLUG_NO_FILESAVE | ||