Diffstat (limited to 'core/multimedia/opieplayer/modplug/load_xm.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/multimedia/opieplayer/modplug/load_xm.cpp | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/modplug/load_xm.cpp b/core/multimedia/opieplayer/modplug/load_xm.cpp new file mode 100644 index 0000000..171e5f6 --- a/dev/null +++ b/core/multimedia/opieplayer/modplug/load_xm.cpp | |||
@@ -0,0 +1,912 @@ | |||
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 | //////////////////////////////////////////////////////// | ||
15 | // FastTracker II XM file support | ||
16 | |||
17 | #ifdef WIN32 | ||
18 | #pragma warning(disable:4244) | ||
19 | #endif | ||
20 | |||
21 | #pragma pack(1) | ||
22 | typedef struct tagXMFILEHEADER | ||
23 | { | ||
24 | DWORD size; | ||
25 | WORD norder; | ||
26 | WORD restartpos; | ||
27 | WORD channels; | ||
28 | WORD patterns; | ||
29 | WORD instruments; | ||
30 | WORD flags; | ||
31 | WORD speed; | ||
32 | WORD tempo; | ||
33 | BYTE order[256]; | ||
34 | } Q_PACKED XMFILEHEADER; | ||
35 | |||
36 | |||
37 | typedef struct tagXMINSTRUMENTHEADER | ||
38 | { | ||
39 | DWORD size; | ||
40 | CHAR name[22]; | ||
41 | BYTE type; | ||
42 | BYTE samples; | ||
43 | BYTE samplesh; | ||
44 | } Q_PACKED XMINSTRUMENTHEADER; | ||
45 | |||
46 | |||
47 | typedef struct tagXMSAMPLEHEADER | ||
48 | { | ||
49 | DWORD shsize; | ||
50 | BYTE snum[96]; | ||
51 | WORD venv[24]; | ||
52 | WORD penv[24]; | ||
53 | BYTE vnum, pnum; | ||
54 | BYTE vsustain, vloops, vloope, psustain, ploops, ploope; | ||
55 | BYTE vtype, ptype; | ||
56 | BYTE vibtype, vibsweep, vibdepth, vibrate; | ||
57 | WORD volfade; | ||
58 | WORD res; | ||
59 | BYTE reserved1[20]; | ||
60 | } Q_PACKED XMSAMPLEHEADER; | ||
61 | |||
62 | typedef struct tagXMSAMPLESTRUCT | ||
63 | { | ||
64 | DWORD samplen; | ||
65 | DWORD loopstart; | ||
66 | DWORD looplen; | ||
67 | BYTE vol; | ||
68 | signed char finetune; | ||
69 | BYTE type; | ||
70 | BYTE pan; | ||
71 | signed char relnote; | ||
72 | BYTE res; | ||
73 | char name[22]; | ||
74 | } Q_PACKED XMSAMPLESTRUCT; | ||
75 | |||
76 | typedef struct tagXMPATTERNHEADER | ||
77 | { | ||
78 | DWORD size; | ||
79 | BYTE packing; | ||
80 | WORD rows; | ||
81 | WORD packsize; | ||
82 | } Q_PACKED XMPATTERNHEADER; | ||
83 | |||
84 | #pragma pack() | ||
85 | |||
86 | |||
87 | BOOL CSoundFile::ReadXM(const BYTE *lpStream, DWORD dwMemLength) | ||
88 | //-------------------------------------------------------------- | ||
89 | { | ||
90 | XMSAMPLEHEADER xmsh; | ||
91 | XMSAMPLESTRUCT xmss; | ||
92 | DWORD dwMemPos, dwHdrSize; | ||
93 | WORD norders=0, restartpos=0, channels=0, patterns=0, instruments=0; | ||
94 | WORD xmflags=0, deftempo=125, defspeed=6; | ||
95 | BOOL InstUsed[256]; | ||
96 | BYTE channels_used[MAX_CHANNELS]; | ||
97 | BYTE pattern_map[256]; | ||
98 | BOOL samples_used[MAX_SAMPLES]; | ||
99 | UINT unused_samples; | ||
100 | |||
101 | m_nChannels = 0; | ||
102 | if ((!lpStream) || (dwMemLength < 0x200)) return FALSE; | ||
103 | if (strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return FALSE; | ||
104 | |||
105 | memcpy(m_szNames[0], lpStream+17, 20); | ||
106 | dwHdrSize = bswapLE32(*((DWORD *)(lpStream+60))); | ||
107 | norders = bswapLE16(*((WORD *)(lpStream+64))); | ||
108 | if ((!norders) || (norders > MAX_ORDERS)) return FALSE; | ||
109 | restartpos = bswapLE16(*((WORD *)(lpStream+66))); | ||
110 | channels = bswapLE16(*((WORD *)(lpStream+68))); | ||
111 | if ((!channels) || (channels > 64)) return FALSE; | ||
112 | m_nType = MOD_TYPE_XM; | ||
113 | m_nMinPeriod = 27; | ||
114 | m_nMaxPeriod = 54784; | ||
115 | m_nChannels = channels; | ||
116 | if (restartpos < norders) m_nRestartPos = restartpos; | ||
117 | patterns = bswapLE16(*((WORD *)(lpStream+70))); | ||
118 | if (patterns > 256) patterns = 256; | ||
119 | instruments = bswapLE16(*((WORD *)(lpStream+72))); | ||
120 | if (instruments >= MAX_INSTRUMENTS) instruments = MAX_INSTRUMENTS-1; | ||
121 | m_nInstruments = instruments; | ||
122 | m_nSamples = 0; | ||
123 | memcpy(&xmflags, lpStream+74, 2); | ||
124 | xmflags = bswapLE16(xmflags); | ||
125 | if (xmflags & 1) m_dwSongFlags |= SONG_LINEARSLIDES; | ||
126 | if (xmflags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; | ||
127 | defspeed = bswapLE16(*((WORD *)(lpStream+76))); | ||
128 | deftempo = bswapLE16(*((WORD *)(lpStream+78))); | ||
129 | if ((deftempo >= 32) && (deftempo < 256)) m_nDefaultTempo = deftempo; | ||
130 | if ((defspeed > 0) && (defspeed < 40)) m_nDefaultSpeed = defspeed; | ||
131 | memcpy(Order, lpStream+80, norders); | ||
132 | memset(InstUsed, 0, sizeof(InstUsed)); | ||
133 | if (patterns > MAX_PATTERNS) | ||
134 | { | ||
135 | UINT i, j; | ||
136 | for (i=0; i<norders; i++) | ||
137 | { | ||
138 | if (Order[i] < patterns) InstUsed[Order[i]] = TRUE; | ||
139 | } | ||
140 | j = 0; | ||
141 | for (i=0; i<256; i++) | ||
142 | { | ||
143 | if (InstUsed[i]) pattern_map[i] = j++; | ||
144 | } | ||
145 | for (i=0; i<256; i++) | ||
146 | { | ||
147 | if (!InstUsed[i]) | ||
148 | { | ||
149 | pattern_map[i] = (j < MAX_PATTERNS) ? j : 0xFE; | ||
150 | j++; | ||
151 | } | ||
152 | } | ||
153 | for (i=0; i<norders; i++) | ||
154 | { | ||
155 | Order[i] = pattern_map[Order[i]]; | ||
156 | } | ||
157 | } else | ||
158 | { | ||
159 | for (UINT i=0; i<256; i++) pattern_map[i] = i; | ||
160 | } | ||
161 | memset(InstUsed, 0, sizeof(InstUsed)); | ||
162 | dwMemPos = dwHdrSize + 60; | ||
163 | if (dwMemPos + 8 >= dwMemLength) return TRUE; | ||
164 | // Reading patterns | ||
165 | memset(channels_used, 0, sizeof(channels_used)); | ||
166 | for (UINT ipat=0; ipat<patterns; ipat++) | ||
167 | { | ||
168 | UINT ipatmap = pattern_map[ipat]; | ||
169 | DWORD dwSize = 0; | ||
170 | WORD rows=64, packsize=0; | ||
171 | XMPATTERNHEADER *patternHeader = (XMPATTERNHEADER *)(lpStream+dwMemPos); | ||
172 | //dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); | ||
173 | dwSize = bswapLE32(patternHeader->size); | ||
174 | /* | ||
175 | while ((dwMemPos + dwSize >= dwMemLength) || (dwSize & 0xFFFFFF00)) | ||
176 | { | ||
177 | if (dwMemPos + 4 >= dwMemLength) break; | ||
178 | dwMemPos++; | ||
179 | dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); | ||
180 | } | ||
181 | */ | ||
182 | //rows = bswapLE16(*((WORD *)(lpStream+dwMemPos+5))); | ||
183 | rows = bswapLE16(patternHeader->rows); | ||
184 | if ((!rows) || (rows > 256)) rows = 64; | ||
185 | //packsize = bswapLE16(*((WORD *)(lpStream+dwMemPos+7))); | ||
186 | packsize = bswapLE16(patternHeader->packsize); | ||
187 | if (dwMemPos + dwSize + 4 > dwMemLength) return TRUE; | ||
188 | dwMemPos += dwSize; | ||
189 | if (dwMemPos + packsize + 4 > dwMemLength) return TRUE; | ||
190 | MODCOMMAND *p; | ||
191 | if (ipatmap < MAX_PATTERNS) | ||
192 | { | ||
193 | PatternSize[ipatmap] = rows; | ||
194 | if ((Patterns[ipatmap] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; | ||
195 | if (!packsize) continue; | ||
196 | p = Patterns[ipatmap]; | ||
197 | } else p = NULL; | ||
198 | const BYTE *src = lpStream+dwMemPos; | ||
199 | UINT j=0; | ||
200 | for (UINT row=0; row<rows; row++) | ||
201 | { | ||
202 | for (UINT chn=0; chn<m_nChannels; chn++) | ||
203 | { | ||
204 | if ((p) && (j < packsize)) | ||
205 | { | ||
206 | BYTE b = src[j++]; | ||
207 | UINT vol = 0; | ||
208 | if (b & 0x80) | ||
209 | { | ||
210 | if (b & 1) p->note = src[j++]; | ||
211 | if (b & 2) p->instr = src[j++]; | ||
212 | if (b & 4) vol = src[j++]; | ||
213 | if (b & 8) p->command = src[j++]; | ||
214 | if (b & 16) p->param = src[j++]; | ||
215 | } else | ||
216 | { | ||
217 | p->note = b; | ||
218 | p->instr = src[j++]; | ||
219 | vol = src[j++]; | ||
220 | p->command = src[j++]; | ||
221 | p->param = src[j++]; | ||
222 | } | ||
223 | if (p->note == 97) p->note = 0xFF; else | ||
224 | if ((p->note) && (p->note < 97)) p->note += 12; | ||
225 | if (p->note) channels_used[chn] = 1; | ||
226 | if (p->command | p->param) ConvertModCommand(p); | ||
227 | if (p->instr == 0xff) p->instr = 0; | ||
228 | if (p->instr) InstUsed[p->instr] = TRUE; | ||
229 | if ((vol >= 0x10) && (vol <= 0x50)) | ||
230 | { | ||
231 | p->volcmd = VOLCMD_VOLUME; | ||
232 | p->vol = vol - 0x10; | ||
233 | } else | ||
234 | if (vol >= 0x60) | ||
235 | { | ||
236 | UINT v = vol & 0xF0; | ||
237 | vol &= 0x0F; | ||
238 | p->vol = vol; | ||
239 | switch(v) | ||
240 | { | ||
241 | // 60-6F: Volume Slide Down | ||
242 | case 0x60:p->volcmd = VOLCMD_VOLSLIDEDOWN; break; | ||
243 | // 70-7F: Volume Slide Up: | ||
244 | case 0x70:p->volcmd = VOLCMD_VOLSLIDEUP; break; | ||
245 | // 80-8F: Fine Volume Slide Down | ||
246 | case 0x80:p->volcmd = VOLCMD_FINEVOLDOWN; break; | ||
247 | // 90-9F: Fine Volume Slide Up | ||
248 | case 0x90:p->volcmd = VOLCMD_FINEVOLUP; break; | ||
249 | // A0-AF: Set Vibrato Speed | ||
250 | case 0xA0:p->volcmd = VOLCMD_VIBRATOSPEED; break; | ||
251 | // B0-BF: Vibrato | ||
252 | case 0xB0:p->volcmd = VOLCMD_VIBRATO; break; | ||
253 | // C0-CF: Set Panning | ||
254 | case 0xC0:p->volcmd = VOLCMD_PANNING; p->vol = (vol << 2) + 2; break; | ||
255 | // D0-DF: Panning Slide Left | ||
256 | case 0xD0:p->volcmd = VOLCMD_PANSLIDELEFT; break; | ||
257 | // E0-EF: Panning Slide Right | ||
258 | case 0xE0:p->volcmd = VOLCMD_PANSLIDERIGHT; break; | ||
259 | // F0-FF: Tone Portamento | ||
260 | case 0xF0:p->volcmd = VOLCMD_TONEPORTAMENTO; break; | ||
261 | } | ||
262 | } | ||
263 | p++; | ||
264 | } else | ||
265 | if (j < packsize) | ||
266 | { | ||
267 | BYTE b = src[j++]; | ||
268 | if (b & 0x80) | ||
269 | { | ||
270 | if (b & 1) j++; | ||
271 | if (b & 2) j++; | ||
272 | if (b & 4) j++; | ||
273 | if (b & 8) j++; | ||
274 | if (b & 16) j++; | ||
275 | } else j += 4; | ||
276 | } else break; | ||
277 | } | ||
278 | } | ||
279 | dwMemPos += packsize; | ||
280 | } | ||
281 | /* | ||
282 | // Wrong offset check | ||
283 | while (dwMemPos + 4 < dwMemLength) | ||
284 | { | ||
285 | DWORD d = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); | ||
286 | if (d < 0x300) break; | ||
287 | dwMemPos++; | ||
288 | } | ||
289 | */ | ||
290 | memset(samples_used, 0, sizeof(samples_used)); | ||
291 | unused_samples = 0; | ||
292 | // Reading instruments | ||
293 | for (UINT iIns=1; iIns<=instruments; iIns++) | ||
294 | { | ||
295 | XMINSTRUMENTHEADER *pih; | ||
296 | BYTE flags[32]; | ||
297 | DWORD samplesize[32]; | ||
298 | UINT samplemap[32]; | ||
299 | WORD nsamples; | ||
300 | |||
301 | if (dwMemPos + sizeof(XMINSTRUMENTHEADER) >= dwMemLength) return TRUE; | ||
302 | pih = (XMINSTRUMENTHEADER *)(lpStream+dwMemPos); | ||
303 | if (dwMemPos + bswapLE32(pih->size) > dwMemLength) return TRUE; | ||
304 | if ((Headers[iIns] = new INSTRUMENTHEADER) == NULL) continue; | ||
305 | memset(Headers[iIns], 0, sizeof(INSTRUMENTHEADER)); | ||
306 | memcpy(Headers[iIns]->name, pih->name, 22); | ||
307 | if ((nsamples = pih->samples) > 0) | ||
308 | { | ||
309 | if (dwMemPos + sizeof(XMSAMPLEHEADER) > dwMemLength) return TRUE; | ||
310 | memcpy(&xmsh, lpStream+dwMemPos+sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER)); | ||
311 | xmsh.shsize = bswapLE32(xmsh.shsize); | ||
312 | for (int i = 0; i < 24; ++i) { | ||
313 | xmsh.venv[i] = bswapLE16(xmsh.venv[i]); | ||
314 | xmsh.penv[i] = bswapLE16(xmsh.penv[i]); | ||
315 | } | ||
316 | xmsh.volfade = bswapLE16(xmsh.volfade); | ||
317 | xmsh.res = bswapLE16(xmsh.res); | ||
318 | dwMemPos += bswapLE32(pih->size); | ||
319 | } else | ||
320 | { | ||
321 | if (bswapLE32(pih->size)) dwMemPos += bswapLE32(pih->size); | ||
322 | else dwMemPos += sizeof(XMINSTRUMENTHEADER); | ||
323 | continue; | ||
324 | } | ||
325 | memset(samplemap, 0, sizeof(samplemap)); | ||
326 | if (nsamples > 32) return TRUE; | ||
327 | UINT newsamples = m_nSamples; | ||
328 | for (UINT nmap=0; nmap<nsamples; nmap++) | ||
329 | { | ||
330 | UINT n = m_nSamples+nmap+1; | ||
331 | if (n >= MAX_SAMPLES) | ||
332 | { | ||
333 | n = m_nSamples; | ||
334 | while (n > 0) | ||
335 | { | ||
336 | if (!Ins[n].pSample) | ||
337 | { | ||
338 | for (UINT xmapchk=0; xmapchk < nmap; xmapchk++) | ||
339 | { | ||
340 | if (samplemap[xmapchk] == n) goto alreadymapped; | ||
341 | } | ||
342 | for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs]) | ||
343 | { | ||
344 | INSTRUMENTHEADER *pks = Headers[clrs]; | ||
345 | for (UINT ks=0; ks<128; ks++) | ||
346 | { | ||
347 | if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; | ||
348 | } | ||
349 | } | ||
350 | break; | ||
351 | } | ||
352 | alreadymapped: | ||
353 | n--; | ||
354 | } | ||
355 | #ifndef FASTSOUNDLIB | ||
356 | // Damn! more than 200 samples: look for duplicates | ||
357 | if (!n) | ||
358 | { | ||
359 | if (!unused_samples) | ||
360 | { | ||
361 | unused_samples = DetectUnusedSamples(samples_used); | ||
362 | if (!unused_samples) unused_samples = 0xFFFF; | ||
363 | } | ||
364 | if ((unused_samples) && (unused_samples != 0xFFFF)) | ||
365 | { | ||
366 | for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext]) | ||
367 | { | ||
368 | unused_samples--; | ||
369 | samples_used[iext] = TRUE; | ||
370 | DestroySample(iext); | ||
371 | n = iext; | ||
372 | for (UINT mapchk=0; mapchk<nmap; mapchk++) | ||
373 | { | ||
374 | if (samplemap[mapchk] == n) samplemap[mapchk] = 0; | ||
375 | } | ||
376 | for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs]) | ||
377 | { | ||
378 | INSTRUMENTHEADER *pks = Headers[clrs]; | ||
379 | for (UINT ks=0; ks<128; ks++) | ||
380 | { | ||
381 | if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; | ||
382 | } | ||
383 | } | ||
384 | memset(&Ins[n], 0, sizeof(Ins[0])); | ||
385 | break; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | #endif // FASTSOUNDLIB | ||
390 | } | ||
391 | if (newsamples < n) newsamples = n; | ||
392 | samplemap[nmap] = n; | ||
393 | } | ||
394 | m_nSamples = newsamples; | ||
395 | // Reading Volume Envelope | ||
396 | INSTRUMENTHEADER *penv = Headers[iIns]; | ||
397 | penv->nMidiProgram = pih->type; | ||
398 | penv->nFadeOut = xmsh.volfade; | ||
399 | penv->nPan = 128; | ||
400 | penv->nPPC = 5*12; | ||
401 | if (xmsh.vtype & 1) penv->dwFlags |= ENV_VOLUME; | ||
402 | if (xmsh.vtype & 2) penv->dwFlags |= ENV_VOLSUSTAIN; | ||
403 | if (xmsh.vtype & 4) penv->dwFlags |= ENV_VOLLOOP; | ||
404 | if (xmsh.ptype & 1) penv->dwFlags |= ENV_PANNING; | ||
405 | if (xmsh.ptype & 2) penv->dwFlags |= ENV_PANSUSTAIN; | ||
406 | if (xmsh.ptype & 4) penv->dwFlags |= ENV_PANLOOP; | ||
407 | if (xmsh.vnum > 12) xmsh.vnum = 12; | ||
408 | if (xmsh.pnum > 12) xmsh.pnum = 12; | ||
409 | penv->nVolEnv = xmsh.vnum; | ||
410 | if (!xmsh.vnum) penv->dwFlags &= ~ENV_VOLUME; | ||
411 | if (!xmsh.pnum) penv->dwFlags &= ~ENV_PANNING; | ||
412 | penv->nPanEnv = xmsh.pnum; | ||
413 | penv->nVolSustainBegin = penv->nVolSustainEnd = xmsh.vsustain; | ||
414 | if (xmsh.vsustain >= 12) penv->dwFlags &= ~ENV_VOLSUSTAIN; | ||
415 | penv->nVolLoopStart = xmsh.vloops; | ||
416 | penv->nVolLoopEnd = xmsh.vloope; | ||
417 | if (penv->nVolLoopEnd >= 12) penv->nVolLoopEnd = 0; | ||
418 | if (penv->nVolLoopStart >= penv->nVolLoopEnd) penv->dwFlags &= ~ENV_VOLLOOP; | ||
419 | penv->nPanSustainBegin = penv->nPanSustainEnd = xmsh.psustain; | ||
420 | if (xmsh.psustain >= 12) penv->dwFlags &= ~ENV_PANSUSTAIN; | ||
421 | penv->nPanLoopStart = xmsh.ploops; | ||
422 | penv->nPanLoopEnd = xmsh.ploope; | ||
423 | if (penv->nPanLoopEnd >= 12) penv->nPanLoopEnd = 0; | ||
424 | if (penv->nPanLoopStart >= penv->nPanLoopEnd) penv->dwFlags &= ~ENV_PANLOOP; | ||
425 | penv->nGlobalVol = 64; | ||
426 | for (UINT ienv=0; ienv<12; ienv++) | ||
427 | { | ||
428 | penv->VolPoints[ienv] = (WORD)xmsh.venv[ienv*2]; | ||
429 | penv->VolEnv[ienv] = (BYTE)xmsh.venv[ienv*2+1]; | ||
430 | penv->PanPoints[ienv] = (WORD)xmsh.penv[ienv*2]; | ||
431 | penv->PanEnv[ienv] = (BYTE)xmsh.penv[ienv*2+1]; | ||
432 | if (ienv) | ||
433 | { | ||
434 | if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) | ||
435 | { | ||
436 | penv->VolPoints[ienv] &= 0xFF; | ||
437 | penv->VolPoints[ienv] += penv->VolPoints[ienv-1] & 0xFF00; | ||
438 | if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) penv->VolPoints[ienv] += 0x100; | ||
439 | } | ||
440 | if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) | ||
441 | { | ||
442 | penv->PanPoints[ienv] &= 0xFF; | ||
443 | penv->PanPoints[ienv] += penv->PanPoints[ienv-1] & 0xFF00; | ||
444 | if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) penv->PanPoints[ienv] += 0x100; | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | for (UINT j=0; j<96; j++) | ||
449 | { | ||
450 | penv->NoteMap[j+12] = j+1+12; | ||
451 | if (xmsh.snum[j] < nsamples) | ||
452 | penv->Keyboard[j+12] = samplemap[xmsh.snum[j]]; | ||
453 | } | ||
454 | // Reading samples | ||
455 | for (UINT ins=0; ins<nsamples; ins++) | ||
456 | { | ||
457 | if ((dwMemPos + sizeof(xmss) > dwMemLength) | ||
458 | || (dwMemPos + xmsh.shsize > dwMemLength)) return TRUE; | ||
459 | memcpy(&xmss, lpStream+dwMemPos, sizeof(xmss)); | ||
460 | xmss.samplen = bswapLE32(xmss.samplen); | ||
461 | xmss.loopstart = bswapLE32(xmss.loopstart); | ||
462 | xmss.looplen = bswapLE32(xmss.looplen); | ||
463 | dwMemPos += xmsh.shsize; | ||
464 | flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D; | ||
465 | if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D; | ||
466 | samplesize[ins] = xmss.samplen; | ||
467 | if (!samplemap[ins]) continue; | ||
468 | if (xmss.type & 0x10) | ||
469 | { | ||
470 | xmss.looplen >>= 1; | ||
471 | xmss.loopstart >>= 1; | ||
472 | xmss.samplen >>= 1; | ||
473 | } | ||
474 | if (xmss.type & 0x20) | ||
475 | { | ||
476 | xmss.looplen >>= 1; | ||
477 | xmss.loopstart >>= 1; | ||
478 | xmss.samplen >>= 1; | ||
479 | } | ||
480 | if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH; | ||
481 | if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3; | ||
482 | xmss.looplen += xmss.loopstart; | ||
483 | if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen; | ||
484 | if (!xmss.looplen) xmss.type &= ~3; | ||
485 | UINT imapsmp = samplemap[ins]; | ||
486 | memcpy(m_szNames[imapsmp], xmss.name, 22); | ||
487 | m_szNames[imapsmp][22] = 0; | ||
488 | MODINSTRUMENT *pins = &Ins[imapsmp]; | ||
489 | pins->nLength = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen; | ||
490 | pins->nLoopStart = xmss.loopstart; | ||
491 | pins->nLoopEnd = xmss.looplen; | ||
492 | if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength; | ||
493 | if (pins->nLoopStart >= pins->nLoopEnd) | ||
494 | { | ||
495 | pins->nLoopStart = pins->nLoopEnd = 0; | ||
496 | } | ||
497 | if (xmss.type & 3) pins->uFlags |= CHN_LOOP; | ||
498 | if (xmss.type & 2) pins->uFlags |= CHN_PINGPONGLOOP; | ||
499 | pins->nVolume = xmss.vol << 2; | ||
500 | if (pins->nVolume > 256) pins->nVolume = 256; | ||
501 | pins->nGlobalVol = 64; | ||
502 | if ((xmss.res == 0xAD) && (!(xmss.type & 0x30))) | ||
503 | { | ||
504 | flags[ins] = RS_ADPCM4; | ||
505 | samplesize[ins] = (samplesize[ins]+1)/2 + 16; | ||
506 | } | ||
507 | pins->nFineTune = xmss.finetune; | ||
508 | pins->RelativeTone = (int)xmss.relnote; | ||
509 | pins->nPan = xmss.pan; | ||
510 | pins->uFlags |= CHN_PANNING; | ||
511 | pins->nVibType = xmsh.vibtype; | ||
512 | pins->nVibSweep = xmsh.vibsweep; | ||
513 | pins->nVibDepth = xmsh.vibdepth; | ||
514 | pins->nVibRate = xmsh.vibrate; | ||
515 | memcpy(pins->name, xmss.name, 22); | ||
516 | pins->name[21] = 0; | ||
517 | } | ||
518 | #if 0 | ||
519 | if ((xmsh.reserved2 > nsamples) && (xmsh.reserved2 <= 16)) | ||
520 | { | ||
521 | dwMemPos += (((UINT)xmsh.reserved2) - nsamples) * xmsh.shsize; | ||
522 | } | ||
523 | #endif | ||
524 | for (UINT ismpd=0; ismpd<nsamples; ismpd++) | ||
525 | { | ||
526 | if ((samplemap[ismpd]) && (samplesize[ismpd]) && (dwMemPos < dwMemLength)) | ||
527 | { | ||
528 | ReadSample(&Ins[samplemap[ismpd]], flags[ismpd], (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); | ||
529 | } | ||
530 | dwMemPos += samplesize[ismpd]; | ||
531 | if (dwMemPos >= dwMemLength) break; | ||
532 | } | ||
533 | } | ||
534 | // Read song comments: "TEXT" | ||
535 | if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x74786574)) | ||
536 | { | ||
537 | UINT len = *((DWORD *)(lpStream+dwMemPos+4)); | ||
538 | dwMemPos += 8; | ||
539 | if ((dwMemPos + len <= dwMemLength) && (len < 16384)) | ||
540 | { | ||
541 | m_lpszSongComments = new char[len+1]; | ||
542 | if (m_lpszSongComments) | ||
543 | { | ||
544 | memcpy(m_lpszSongComments, lpStream+dwMemPos, len); | ||
545 | m_lpszSongComments[len] = 0; | ||
546 | } | ||
547 | dwMemPos += len; | ||
548 | } | ||
549 | } | ||
550 | // Read midi config: "MIDI" | ||
551 | if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4944494D)) | ||
552 | { | ||
553 | UINT len = *((DWORD *)(lpStream+dwMemPos+4)); | ||
554 | dwMemPos += 8; | ||
555 | if (len == sizeof(MODMIDICFG)) | ||
556 | { | ||
557 | memcpy(&m_MidiCfg, lpStream+dwMemPos, len); | ||
558 | m_dwSongFlags |= SONG_EMBEDMIDICFG; | ||
559 | } | ||
560 | } | ||
561 | // Read pattern names: "PNAM" | ||
562 | if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e50)) | ||
563 | { | ||
564 | UINT len = *((DWORD *)(lpStream+dwMemPos+4)); | ||
565 | dwMemPos += 8; | ||
566 | if ((dwMemPos + len <= dwMemLength) && (len <= MAX_PATTERNS*MAX_PATTERNNAME) && (len >= MAX_PATTERNNAME)) | ||
567 | { | ||
568 | m_lpszPatternNames = new char[len]; | ||
569 | |||
570 | if (m_lpszPatternNames) | ||
571 | { | ||
572 | m_nPatternNames = len / MAX_PATTERNNAME; | ||
573 | memcpy(m_lpszPatternNames, lpStream+dwMemPos, len); | ||
574 | } | ||
575 | dwMemPos += len; | ||
576 | } | ||
577 | } | ||
578 | // Read channel names: "CNAM" | ||
579 | if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e43)) | ||
580 | { | ||
581 | UINT len = *((DWORD *)(lpStream+dwMemPos+4)); | ||
582 | dwMemPos += 8; | ||
583 | if ((dwMemPos + len <= dwMemLength) && (len <= MAX_BASECHANNELS*MAX_CHANNELNAME)) | ||
584 | { | ||
585 | UINT n = len / MAX_CHANNELNAME; | ||
586 | for (UINT i=0; i<n; i++) | ||
587 | { | ||
588 | memcpy(ChnSettings[i].szName, (lpStream+dwMemPos+i*MAX_CHANNELNAME), MAX_CHANNELNAME); | ||
589 | ChnSettings[i].szName[MAX_CHANNELNAME-1] = 0; | ||
590 | } | ||
591 | dwMemPos += len; | ||
592 | } | ||
593 | } | ||
594 | // Read mix plugins information | ||
595 | if (dwMemPos + 8 < dwMemLength) | ||
596 | { | ||
597 | dwMemPos += LoadMixPlugins(lpStream+dwMemPos, dwMemLength-dwMemPos); | ||
598 | } | ||
599 | return TRUE; | ||
600 | } | ||
601 | |||
602 | |||
603 | #ifndef MODPLUG_NO_FILESAVE | ||
604 | |||
605 | BOOL CSoundFile::SaveXM(LPCSTR lpszFileName, UINT nPacking) | ||
606 | //--------------------------------------------------------- | ||
607 | { | ||
608 | BYTE s[64*64*5]; | ||
609 | XMFILEHEADER header; | ||
610 | XMINSTRUMENTHEADER xmih; | ||
611 | XMSAMPLEHEADER xmsh; | ||
612 | XMSAMPLESTRUCT xmss; | ||
613 | BYTE smptable[32]; | ||
614 | BYTE xmph[9]; | ||
615 | FILE *f; | ||
616 | int i; | ||
617 | |||
618 | if ((!m_nChannels) || (!lpszFileName)) return FALSE; | ||
619 | if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; | ||
620 | fwrite("Extended Module: ", 17, 1, f); | ||
621 | fwrite(m_szNames[0], 20, 1, f); | ||
622 | s[0] = 0x1A; | ||
623 | lstrcpy((LPSTR)&s[1], (nPacking) ? "MOD Plugin packed " : "FastTracker v2.00 "); | ||
624 | s[21] = 0x04; | ||
625 | s[22] = 0x01; | ||
626 | fwrite(s, 23, 1, f); | ||
627 | // Writing song header | ||
628 | memset(&header, 0, sizeof(header)); | ||
629 | header.size = sizeof(XMFILEHEADER); | ||
630 | header.norder = 0; | ||
631 | header.restartpos = m_nRestartPos; | ||
632 | header.channels = m_nChannels; | ||
633 | header.patterns = 0; | ||
634 | for (i=0; i<MAX_ORDERS; i++) | ||
635 | { | ||
636 | if (Order[i] == 0xFF) break; | ||
637 | header.norder++; | ||
638 | if ((Order[i] >= header.patterns) && (Order[i] < MAX_PATTERNS)) header.patterns = Order[i]+1; | ||
639 | } | ||
640 | header.instruments = m_nInstruments; | ||
641 | if (!header.instruments) header.instruments = m_nSamples; | ||
642 | header.flags = (m_dwSongFlags & SONG_LINEARSLIDES) ? 0x01 : 0x00; | ||
643 | if (m_dwSongFlags & SONG_EXFILTERRANGE) header.flags |= 0x1000; | ||
644 | header.tempo = m_nDefaultTempo; | ||
645 | header.speed = m_nDefaultSpeed; | ||
646 | memcpy(header.order, Order, header.norder); | ||
647 | fwrite(&header, 1, sizeof(header), f); | ||
648 | // Writing patterns | ||
649 | for (i=0; i<header.patterns; i++) if (Patterns[i]) | ||
650 | { | ||
651 | MODCOMMAND *p = Patterns[i]; | ||
652 | UINT len = 0; | ||
653 | |||
654 | memset(&xmph, 0, sizeof(xmph)); | ||
655 | xmph[0] = 9; | ||
656 | xmph[5] = (BYTE)(PatternSize[i] & 0xFF); | ||
657 | xmph[6] = (BYTE)(PatternSize[i] >> 8); | ||
658 | for (UINT j=m_nChannels*PatternSize[i]; j; j--,p++) | ||
659 | { | ||
660 | UINT note = p->note; | ||
661 | UINT param = ModSaveCommand(p, TRUE); | ||
662 | UINT command = param >> 8; | ||
663 | param &= 0xFF; | ||
664 | if (note >= 0xFE) note = 97; else | ||
665 | if ((note <= 12) || (note > 96+12)) note = 0; else | ||
666 | note -= 12; | ||
667 | UINT vol = 0; | ||
668 | if (p->volcmd) | ||
669 | { | ||
670 | UINT volcmd = p->volcmd; | ||
671 | switch(volcmd) | ||
672 | { | ||
673 | case VOLCMD_VOLUME: vol = 0x10 + p->vol; break; | ||
674 | case VOLCMD_VOLSLIDEDOWN:vol = 0x60 + (p->vol & 0x0F); break; | ||
675 | case VOLCMD_VOLSLIDEUP: vol = 0x70 + (p->vol & 0x0F); break; | ||
676 | case VOLCMD_FINEVOLDOWN:vol = 0x80 + (p->vol & 0x0F); break; | ||
677 | case VOLCMD_FINEVOLUP: vol = 0x90 + (p->vol & 0x0F); break; | ||
678 | case VOLCMD_VIBRATOSPEED:vol = 0xA0 + (p->vol & 0x0F); break; | ||
679 | case VOLCMD_VIBRATO: vol = 0xB0 + (p->vol & 0x0F); break; | ||
680 | case VOLCMD_PANNING: vol = 0xC0 + (p->vol >> 2); if (vol > 0xCF) vol = 0xCF; break; | ||
681 | case VOLCMD_PANSLIDELEFT:vol = 0xD0 + (p->vol & 0x0F); break; | ||
682 | case VOLCMD_PANSLIDERIGHT:vol = 0xE0 + (p->vol & 0x0F); break; | ||
683 | case VOLCMD_TONEPORTAMENTO:vol = 0xF0 + (p->vol & 0x0F); break; | ||
684 | } | ||
685 | } | ||
686 | if ((note) && (p->instr) && (vol > 0x0F) && (command) && (param)) | ||
687 | { | ||
688 | s[len++] = note; | ||
689 | s[len++] = p->instr; | ||
690 | s[len++] = vol; | ||
691 | s[len++] = command; | ||
692 | s[len++] = param; | ||
693 | } else | ||
694 | { | ||
695 | BYTE b = 0x80; | ||
696 | if (note) b |= 0x01; | ||
697 | if (p->instr) b |= 0x02; | ||
698 | if (vol >= 0x10) b |= 0x04; | ||
699 | if (command) b |= 0x08; | ||
700 | if (param) b |= 0x10; | ||
701 | s[len++] = b; | ||
702 | if (b & 1) s[len++] = note; | ||
703 | if (b & 2) s[len++] = p->instr; | ||
704 | if (b & 4) s[len++] = vol; | ||
705 | if (b & 8) s[len++] = command; | ||
706 | if (b & 16) s[len++] = param; | ||
707 | } | ||
708 | if (len > sizeof(s) - 5) break; | ||
709 | } | ||
710 | xmph[7] = (BYTE)(len & 0xFF); | ||
711 | xmph[8] = (BYTE)(len >> 8); | ||
712 | fwrite(xmph, 1, 9, f); | ||
713 | fwrite(s, 1, len, f); | ||
714 | } else | ||
715 | { | ||
716 | memset(&xmph, 0, sizeof(xmph)); | ||
717 | xmph[0] = 9; | ||
718 | xmph[5] = (BYTE)(PatternSize[i] & 0xFF); | ||
719 | xmph[6] = (BYTE)(PatternSize[i] >> 8); | ||
720 | fwrite(xmph, 1, 9, f); | ||
721 | } | ||
722 | // Writing instruments | ||
723 | for (i=1; i<=header.instruments; i++) | ||
724 | { | ||
725 | MODINSTRUMENT *pins; | ||
726 | BYTE flags[32]; | ||
727 | |||
728 | memset(&xmih, 0, sizeof(xmih)); | ||
729 | memset(&xmsh, 0, sizeof(xmsh)); | ||
730 | xmih.size = sizeof(xmih) + sizeof(xmsh); | ||
731 | memcpy(xmih.name, m_szNames[i], 22); | ||
732 | xmih.type = 0; | ||
733 | xmih.samples = 0; | ||
734 | if (m_nInstruments) | ||
735 | { | ||
736 | INSTRUMENTHEADER *penv = Headers[i]; | ||
737 | if (penv) | ||
738 | { | ||
739 | memcpy(xmih.name, penv->name, 22); | ||
740 | xmih.type = penv->nMidiProgram; | ||
741 | xmsh.volfade = penv->nFadeOut; | ||
742 | xmsh.vnum = (BYTE)penv->nVolEnv; | ||
743 | xmsh.pnum = (BYTE)penv->nPanEnv; | ||
744 | if (xmsh.vnum > 12) xmsh.vnum = 12; | ||
745 | if (xmsh.pnum > 12) xmsh.pnum = 12; | ||
746 | for (UINT ienv=0; ienv<12; ienv++) | ||
747 | { | ||
748 | xmsh.venv[ienv*2] = penv->VolPoints[ienv]; | ||
749 | xmsh.venv[ienv*2+1] = penv->VolEnv[ienv]; | ||
750 | xmsh.penv[ienv*2] = penv->PanPoints[ienv]; | ||
751 | xmsh.penv[ienv*2+1] = penv->PanEnv[ienv]; | ||
752 | } | ||
753 | if (penv->dwFlags & ENV_VOLUME) xmsh.vtype |= 1; | ||
754 | if (penv->dwFlags & ENV_VOLSUSTAIN) xmsh.vtype |= 2; | ||
755 | if (penv->dwFlags & ENV_VOLLOOP) xmsh.vtype |= 4; | ||
756 | if (penv->dwFlags & ENV_PANNING) xmsh.ptype |= 1; | ||
757 | if (penv->dwFlags & ENV_PANSUSTAIN) xmsh.ptype |= 2; | ||
758 | if (penv->dwFlags & ENV_PANLOOP) xmsh.ptype |= 4; | ||
759 | xmsh.vsustain = (BYTE)penv->nVolSustainBegin; | ||
760 | xmsh.vloops = (BYTE)penv->nVolLoopStart; | ||
761 | xmsh.vloope = (BYTE)penv->nVolLoopEnd; | ||
762 | xmsh.psustain = (BYTE)penv->nPanSustainBegin; | ||
763 | xmsh.ploops = (BYTE)penv->nPanLoopStart; | ||
764 | xmsh.ploope = (BYTE)penv->nPanLoopEnd; | ||
765 | for (UINT j=0; j<96; j++) if (penv->Keyboard[j+12]) | ||
766 | { | ||
767 | UINT k; | ||
768 | for (k=0; k<xmih.samples; k++)if (smptable[k] == penv->Keyboard[j+12]) break; | ||
769 | if (k == xmih.samples) | ||
770 | { | ||
771 | smptable[xmih.samples++] = penv->Keyboard[j+12]; | ||
772 | } | ||
773 | if (xmih.samples >= 32) break; | ||
774 | xmsh.snum[j] = k; | ||
775 | } | ||
776 | // xmsh.reserved2 = xmih.samples; | ||
777 | } | ||
778 | } else | ||
779 | { | ||
780 | xmih.samples = 1; | ||
781 | // xmsh.reserved2 = 1; | ||
782 | smptable[0] = i; | ||
783 | } | ||
784 | xmsh.shsize = (xmih.samples) ? 40 : 0; | ||
785 | fwrite(&xmih, 1, sizeof(xmih), f); | ||
786 | if (smptable[0]) | ||
787 | { | ||
788 | MODINSTRUMENT *pvib = &Ins[smptable[0]]; | ||
789 | xmsh.vibtype = pvib->nVibType; | ||
790 | xmsh.vibsweep = pvib->nVibSweep; | ||
791 | xmsh.vibdepth = pvib->nVibDepth; | ||
792 | xmsh.vibrate = pvib->nVibRate; | ||
793 | } | ||
794 | fwrite(&xmsh, 1, xmih.size - sizeof(xmih), f); | ||
795 | if (!xmih.samples) continue; | ||
796 | for (UINT ins=0; ins<xmih.samples; ins++) | ||
797 | { | ||
798 | memset(&xmss, 0, sizeof(xmss)); | ||
799 | if (smptable[ins]) memcpy(xmss.name, m_szNames[smptable[ins]], 22); | ||
800 | pins = &Ins[smptable[ins]]; | ||
801 | xmss.samplen = pins->nLength; | ||
802 | xmss.loopstart = pins->nLoopStart; | ||
803 | xmss.looplen = pins->nLoopEnd - pins->nLoopStart; | ||
804 | xmss.vol = pins->nVolume / 4; | ||
805 | xmss.finetune = (char)pins->nFineTune; | ||
806 | xmss.type = 0; | ||
807 | if (pins->uFlags & CHN_LOOP) xmss.type = (pins->uFlags & CHN_PINGPONGLOOP) ? 2 : 1; | ||
808 | flags[ins] = RS_PCM8D; | ||
809 | #ifndef NO_PACKING | ||
810 | if (nPacking) | ||
811 | { | ||
812 | if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) | ||
813 | && (CanPackSample(pins->pSample, pins->nLength, nPacking))) | ||
814 | { | ||
815 | flags[ins] = RS_ADPCM4; | ||
816 | xmss.res = 0xAD; | ||
817 | } | ||
818 | } else | ||
819 | #endif | ||
820 | { | ||
821 | if (pins->uFlags & CHN_16BIT) | ||
822 | { | ||
823 | flags[ins] = RS_PCM16D; | ||
824 | xmss.type |= 0x10; | ||
825 | xmss.looplen *= 2; | ||
826 | xmss.loopstart *= 2; | ||
827 | xmss.samplen *= 2; | ||
828 | } | ||
829 | if (pins->uFlags & CHN_STEREO) | ||
830 | { | ||
831 | flags[ins] = (pins->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; | ||
832 | xmss.type |= 0x20; | ||
833 | xmss.looplen *= 2; | ||
834 | xmss.loopstart *= 2; | ||
835 | xmss.samplen *= 2; | ||
836 | } | ||
837 | } | ||
838 | xmss.pan = 255; | ||
839 | if (pins->nPan < 256) xmss.pan = (BYTE)pins->nPan; | ||
840 | xmss.relnote = (signed char)pins->RelativeTone; | ||
841 | fwrite(&xmss, 1, xmsh.shsize, f); | ||
842 | } | ||
843 | for (UINT ismpd=0; ismpd<xmih.samples; ismpd++) | ||
844 | { | ||
845 | pins = &Ins[smptable[ismpd]]; | ||
846 | if (pins->pSample) | ||
847 | { | ||
848 | #ifndef NO_PACKING | ||
849 | if ((flags[ismpd] == RS_ADPCM4) && (xmih.samples>1)) CanPackSample(pins->pSample, pins->nLength, nPacking); | ||
850 | #endif // NO_PACKING | ||
851 | WriteSample(f, pins, flags[ismpd]); | ||
852 | } | ||
853 | } | ||
854 | } | ||
855 | // Writing song comments | ||
856 | if ((m_lpszSongComments) && (m_lpszSongComments[0])) | ||
857 | { | ||
858 | DWORD d = 0x74786574; | ||
859 | fwrite(&d, 1, 4, f); | ||
860 | d = strlen(m_lpszSongComments); | ||
861 | fwrite(&d, 1, 4, f); | ||
862 | fwrite(m_lpszSongComments, 1, d, f); | ||
863 | } | ||
864 | // Writing midi cfg | ||
865 | if (m_dwSongFlags & SONG_EMBEDMIDICFG) | ||
866 | { | ||
867 | DWORD d = 0x4944494D; | ||
868 | fwrite(&d, 1, 4, f); | ||
869 | d = sizeof(MODMIDICFG); | ||
870 | fwrite(&d, 1, 4, f); | ||
871 | fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f); | ||
872 | } | ||
873 | // Writing Pattern Names | ||
874 | if ((m_nPatternNames) && (m_lpszPatternNames)) | ||
875 | { | ||
876 | DWORD dwLen = m_nPatternNames * MAX_PATTERNNAME; | ||
877 | while ((dwLen >= MAX_PATTERNNAME) && (!m_lpszPatternNames[dwLen-MAX_PATTERNNAME])) dwLen -= MAX_PATTERNNAME; | ||
878 | if (dwLen >= MAX_PATTERNNAME) | ||
879 | { | ||
880 | DWORD d = 0x4d414e50; | ||
881 | fwrite(&d, 1, 4, f); | ||
882 | fwrite(&dwLen, 1, 4, f); | ||
883 | fwrite(m_lpszPatternNames, 1, dwLen, f); | ||
884 | } | ||
885 | } | ||
886 | // Writing Channel Names | ||
887 | { | ||
888 | UINT nChnNames = 0; | ||
889 | for (UINT inam=0; inam<m_nChannels; inam++) | ||
890 | { | ||
891 | if (ChnSettings[inam].szName[0]) nChnNames = inam+1; | ||
892 | } | ||
893 | // Do it! | ||
894 | if (nChnNames) | ||
895 | { | ||
896 | DWORD dwLen = nChnNames * MAX_CHANNELNAME; | ||
897 | DWORD d = 0x4d414e43; | ||
898 | fwrite(&d, 1, 4, f); | ||
899 | fwrite(&dwLen, 1, 4, f); | ||
900 | for (UINT inam=0; inam<nChnNames; inam++) | ||
901 | { | ||
902 | fwrite(ChnSettings[inam].szName, 1, MAX_CHANNELNAME, f); | ||
903 | } | ||
904 | } | ||
905 | } | ||
906 | // Save mix plugins information | ||
907 | SaveMixPlugins(f); | ||
908 | fclose(f); | ||
909 | return TRUE; | ||
910 | } | ||
911 | |||
912 | #endif // MODPLUG_NO_FILESAVE | ||