1 /*
2 * Copyright (c) 2009, The MilkyTracker Team.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * ExporterXM.cpp
32 * MilkyPlay XM writer (trying hard to export something useful)
33 *
34 * --------------------------------
35 * Version History:
36 * --------------------------------
37 * 26/03/06: PLM Far position jump fix & effects are relocated to other channels if possible
38 * 01/01/06: Happy new year ;) MilkyTracker XMs are now even smaller than FT2 XMs
39 * 02/12/05: Fixed problems with last operand restoration for S3M and MDL commands. VERY SLOW(!!) but working
40 * 06/02/05: Added simulation of multitracker behaviour with FT2 features
41 * 05/02/05: Added simulation of protracker behaviour with FT2 features
42 * 07/12/04: Writes all other instrument related stuff too (envelopes, auto-vibrato etc.)
43 * 06/12/04: Added sample relative note remapper when note range exceeds regular XM note range (RIGHT.PTM)
44 * 05/12/04: First acceptable results with one-effect modules (.MOD / .MTM)
45 * 04/12/04: Started work
46 *
47 */
48 #include "MilkyPlay.h"
49
50 #ifdef VERBOSE
51 #include "stdio.h"
52 #endif
53
54 struct TWorkBuffers
55 {
56 mp_sint32 noteRangeRemapper[256];
57 mp_ubyte lastArpeggio[256];
58 mp_ubyte lastVolSlide[256];
59 mp_ubyte lastGVolSlide[256];
60 mp_ubyte lastPorta[256];
61 mp_ubyte lastTempoSlide[256];
62 mp_sint32 globalVolume;
63 mp_sint32 bpm, baseBpm, speed;
64
65 mp_ubyte lastIns[256];
66 mp_ubyte lastNote[256];
67
clearBuffersTWorkBuffers68 void clearBuffers()
69 {
70 memset(lastArpeggio, 0, sizeof(lastArpeggio));
71 memset(lastVolSlide, 0, sizeof(lastVolSlide));
72 memset(lastGVolSlide, 0, sizeof(lastGVolSlide));
73 memset(lastPorta, 0, sizeof(lastPorta));
74 memset(lastTempoSlide, 0, sizeof(lastTempoSlide));
75 memset(lastIns, 0, sizeof(lastIns));
76 memset(lastNote, 0, sizeof(lastNote));
77 globalVolume = 64;
78 baseBpm = 125;
79 bpm = 125;
80 speed = 6;
81 }
82
TWorkBuffersTWorkBuffers83 TWorkBuffers()
84 {
85 clearBuffers();
86
87 memset(noteRangeRemapper, 0, sizeof(noteRangeRemapper));
88 }
89 };
90
convertEffect(mp_ubyte effIn,mp_ubyte opIn,mp_ubyte & effOut,mp_ubyte & opOut,mp_sint32 curChan,TWorkBuffers & workBuffers,bool convertITTempoSlides)91 static void convertEffect(mp_ubyte effIn, mp_ubyte opIn, mp_ubyte& effOut, mp_ubyte& opOut, mp_sint32 curChan, TWorkBuffers& workBuffers, bool convertITTempoSlides)
92 {
93 mp_ubyte* lastArpeggio = workBuffers.lastArpeggio;
94 mp_ubyte* lastVolSlide = workBuffers.lastVolSlide;
95 mp_ubyte* lastGVolSlide = workBuffers.lastGVolSlide;
96 mp_ubyte* lastPorta = workBuffers.lastPorta;
97 mp_ubyte* lastTempoSlide = workBuffers.lastTempoSlide;
98
99 effOut = opOut = 0;
100
101 // Protracker commands
102 if (effIn > 0 && effIn <= 0x11)
103 {
104 effOut = effIn;
105 opOut = opIn;
106
107 if (effIn == 0x0C || effIn == 0x10)
108 opOut = (mp_ubyte)(((mp_sint32)opOut*64)/255);
109
110 if (effIn == 0x10)
111 workBuffers.globalVolume = (mp_ubyte)(((mp_sint32)opOut*64)/255);
112
113 // Cope with set BPM in case we're having a DBM "set real BPM command"
114 if (effIn == 0x0f && opIn >= 32)
115 {
116 // real BPM is not the default value => recalculate BPM
117 if (workBuffers.baseBpm != 125)
118 {
119 // First store current BPM
120 workBuffers.bpm = opIn;
121 // Now calculate new BPM (see DBM player source)
122 mp_sint32 realCiaTempo = (workBuffers.bpm * (workBuffers.baseBpm << 8) / 125) >> 8;
123 // clip if necessary
124 if (realCiaTempo > 255)
125 realCiaTempo = 255;
126 if (realCiaTempo < 32)
127 realCiaTempo = 32;
128 // This is our new set BPM
129 opOut = realCiaTempo;
130 }
131 }
132
133 }
134 // set envelope position
135 else if (effIn == 0x14 || effIn == 0x51)
136 {
137 effOut = 0x14;
138 opOut = opIn;
139 }
140 // set envelope position
141 else if (effIn == 0x15)
142 {
143 effOut = effIn;
144 opOut = opIn;
145 }
146 // set tempo
147 else if (effIn == 0x16)
148 {
149 if (opIn >= 32)
150 {
151 // First store current BPM
152 workBuffers.bpm = opIn;
153 // Cope with set BPM in case we're having a DBM "set real BPM command"
154 if (workBuffers.baseBpm != 125)
155 {
156 // Now calculate new BPM (see DBM player source)
157 mp_sint32 realCiaTempo = (workBuffers.bpm * (workBuffers.baseBpm << 8) / 125) >> 8;
158 // clip
159 if (realCiaTempo > 255)
160 realCiaTempo = 255;
161 if (realCiaTempo < 32)
162 realCiaTempo = 32;
163 // new effect 0xF with new BPM
164 effOut = 0x0F;
165 opOut = realCiaTempo;
166 }
167 else
168 {
169 if (opIn < 32) opIn = 32;
170 effOut = 0x0F;
171 opOut = opIn;
172 }
173 }
174 else if (convertITTempoSlides)
175 {
176 if (opIn) lastTempoSlide[curChan] = opIn;
177
178 if (lastTempoSlide[curChan])
179 {
180 mp_ubyte y = lastTempoSlide[curChan]>>4;
181 mp_ubyte x = lastTempoSlide[curChan]&0xf;
182
183 // tempo slide up
184 if (y)
185 {
186 effOut = 0x0F;
187 workBuffers.bpm+=x*(workBuffers.speed-1);
188 if (workBuffers.bpm > 255)
189 workBuffers.bpm = 255;
190 opOut = workBuffers.bpm;
191 }
192 // tempo slide down
193 else
194 {
195 effOut = 0xF;
196 workBuffers.bpm-=x*(workBuffers.speed-1);
197 if (workBuffers.bpm < 32)
198 workBuffers.bpm = 32;
199 opOut = workBuffers.bpm;
200 }
201 }
202 }
203 }
204 // Panning slide / Multi retrig / tremor
205 else if (effIn == 0x19 || effIn == 0x1B || effIn == 0x1D)
206 {
207 effOut = effIn;
208 opOut = opIn;
209 }
210 // set speed
211 else if (effIn == 0x1C)
212 {
213 if (opIn)
214 {
215 if (opIn > 31) opIn = 31;
216 effOut = 0x0F;
217 opOut = opIn;
218 }
219 }
220 // MDL set sample offset
221 else if (effIn == 0x1F)
222 {
223 effOut = 0x09;
224 opOut = opIn;
225 }
226 // PLM position jump
227 else if (effIn == 0x2B)
228 {
229 effOut = 0x0B;
230 opOut = opIn;
231 }
232 // Protracker subcommands (most likely)
233 else if (effIn >= 0x30 && effIn <= 0x3F)
234 {
235 effOut = 0x0E;
236 opOut = ((effIn-0x30)<<4)+(opIn&0xF);
237 }
238 // arpeggio
239 else if (effIn == 0x20)
240 {
241 if (!opIn) opIn = lastArpeggio[curChan];
242
243 if (opIn)
244 {
245 effOut = 0;
246 opOut = opIn;
247 lastArpeggio[curChan] = opIn;
248 }
249
250 }
251 // extra fine porta commands
252 else if (effIn == 0x41)
253 {
254 effOut = 0x21;
255 opOut = 0x10 + (opIn <= 0xf ? opIn&0xF : 0xF);
256 }
257 else if (effIn == 0x42)
258 {
259 effOut = 0x21;
260 opOut = 0x20 + (opIn <= 0xf ? opIn&0xF : 0xF);
261 }
262 // MDL porta up
263 else if (effIn == 0x43)
264 {
265 if (opIn) lastPorta[curChan] = opIn;
266
267 if (lastPorta[curChan] >= 0xE0) {
268 mp_ubyte y = lastPorta[curChan]>>4;
269 mp_ubyte x = lastPorta[curChan]&0xf;
270 switch (y) {
271 case 0xF:
272 effOut = 0xE;
273 opOut = 0x10 + x;
274 break;
275 case 0xE:
276 effOut = 0x21;
277 opOut = 0x10 + (x>>1);
278 break;
279 }
280 }
281 else if (lastPorta[curChan])
282 {
283 effOut = 0x1;
284 opOut = lastPorta[curChan];
285 }
286 }
287 // MDL porta down
288 else if (effIn == 0x44)
289 {
290 if (opIn) lastPorta[curChan] = opIn;
291
292 if (lastPorta[curChan] >= 0xE0) {
293 mp_ubyte y = lastPorta[curChan] >> 4;
294 mp_ubyte x = lastPorta[curChan] & 0xf;
295 switch (y) {
296 case 0xF:
297 effOut = 0xE;
298 opOut = 0x20 + x;
299 break;
300 case 0xE:
301 effOut = 0x21;
302 opOut = 0x20 + (x>>1);
303 break;
304 }
305 }
306 else if (lastPorta[curChan])
307 {
308 effOut = 0x2;
309 opOut = lastPorta[curChan];
310 }
311 }
312 // MDL volslide up
313 else if (effIn == 0x45)
314 {
315 if (opIn) lastVolSlide[curChan] = opIn;
316
317 if (lastVolSlide[curChan] >= 0xE0) {
318 mp_ubyte y = lastVolSlide[curChan]>>4;
319 mp_ubyte x = lastVolSlide[curChan]&0xf;
320 switch (y) {
321 case 0xF:
322 effOut = 0xE;
323 opOut = 0xA0 + x;
324 break;
325 case 0xE:
326 effOut = 0xE;
327 opOut = 0xA0 + (x>>2);
328 break;
329 }
330 }
331 else if (lastVolSlide[curChan])
332 {
333 effOut = 0xA;
334 opOut = (lastVolSlide[curChan]>>2) <= 0xF ? (lastVolSlide[curChan]>>2)<<4 : 0xF0;
335 }
336 }
337 // MDL volslide down
338 else if (effIn == 0x46)
339 {
340 if (opIn) lastVolSlide[curChan] = opIn;
341
342 if (lastVolSlide[curChan] >= 0xE0) {
343 mp_ubyte y = lastVolSlide[curChan] >> 4;
344 mp_ubyte x = lastVolSlide[curChan] & 0xf;
345 switch (y) {
346 case 0xF:
347 effOut = 0xE;
348 opOut = 0xB0 + x;
349 break;
350 case 0xE:
351 effOut = 0xE;
352 opOut = 0xB0 + (x>>2);
353 break;
354 }
355 }
356 else if (lastVolSlide[curChan])
357 {
358 effOut = 0xA;
359 opOut = (lastVolSlide[curChan]>>2) <= 0xF ? (lastVolSlide[curChan]>>2) : 0xF;
360 }
361 }
362 // S3M porta up
363 else if (effIn == 0x47)
364 {
365 if (opIn) lastPorta[curChan] = opIn;
366 if (lastPorta[curChan] >= 0xE0) {
367 mp_ubyte y = lastPorta[curChan] >> 4;
368 mp_ubyte x = lastPorta[curChan] & 0xf;
369 switch (y) {
370 case 0xF:
371 effOut = 0xE;
372 opOut = 0x10 + x;
373 break;
374 case 0xE:
375 effOut = 0x21;
376 opOut = 0x10 + x;
377 break;
378 }
379 }
380 else if (lastPorta[curChan])
381 {
382 effOut = 0x1;
383 opOut = lastPorta[curChan];
384 }
385 }
386 // S3M porta down
387 else if (effIn == 0x48)
388 {
389 if (opIn) lastPorta[curChan] = opIn;
390 if (lastPorta[curChan] >= 0xE0) {
391 mp_ubyte y = lastPorta[curChan] >> 4;
392 mp_ubyte x = lastPorta[curChan] & 0xf;
393 switch (y) {
394 case 0xF:
395 effOut = 0xE;
396 opOut = 0x20 + x;
397 break;
398 case 0xE:
399 effOut = 0x21;
400 opOut = 0x20 + x;
401 break;
402 }
403 }
404 else if (lastPorta[curChan])
405 {
406 effOut = 0x2;
407 opOut = lastPorta[curChan];
408 }
409 }
410 // S3M volslide
411 else if (effIn == 0x49)
412 {
413 if (opIn) lastVolSlide[curChan] = opIn;
414
415 if (lastVolSlide[curChan])
416 {
417 mp_ubyte y = lastVolSlide[curChan]>>4;
418 mp_ubyte x = lastVolSlide[curChan]&0xf;
419
420 if (!(x == 0xF && y)&&!(y == 0xF && x))
421 {
422 if (x && y) x = 0;
423
424 if (y)
425 {
426 effOut = 0xA;
427 opOut = y<<4;
428 }
429 else if (x)
430 {
431 effOut = 0xA;
432 opOut = x;
433 }
434
435 }
436 else
437 {
438 if (!(x==0x0F && !y) && !(y==0x0F && !x))
439 {
440 if (x==0x0F)
441 {
442 effOut = 0xE;
443 opOut = 0xA0+y;
444 }
445 else if (y==0x0F)
446 {
447 effOut = 0xE;
448 opOut = 0xB0+x;
449 }
450 }
451 }
452 }
453 }
454 // PSM fine volslide up
455 else if (effIn == 0x4B)
456 {
457 effOut = 0x0E;
458 opOut = opIn>>2;
459 if (opOut>0xF) opOut=0x0F;
460 opOut+=0xA0;
461 }
462 // PSM fine volslide down
463 else if (effIn == 0x4C)
464 {
465 effOut = 0x0E;
466 opOut = opIn>>2;
467 if (opOut>0xF) opOut=0x0F;
468 opOut+=0xB0;
469 }
470 // PSM porta up
471 else if (effIn == 0x4D)
472 {
473 effOut = 0x01;
474 opOut = opIn>>2;
475 }
476 // PSM porta down
477 else if (effIn == 0x4E)
478 {
479 effOut = 0x02;
480 opOut = opIn>>2;
481 }
482 // "S3M" global volslide, this is an IT/MODPLUG feature
483 else if (effIn == 0x59)
484 {
485 if (opIn) lastGVolSlide[curChan] = opIn;
486
487 if (lastGVolSlide[curChan])
488 {
489 mp_ubyte y = lastGVolSlide[curChan]>>4;
490 mp_ubyte x = lastGVolSlide[curChan]&0xf;
491
492 if (!(x == 0xF && y)&&!(y == 0xF && x))
493 {
494 if (x && y) x = 0;
495
496 if (y)
497 {
498 effOut = 0x11;
499 opOut = y<<4;
500 workBuffers.globalVolume+=y*(workBuffers.speed-1);
501 if (workBuffers.globalVolume > 64)
502 workBuffers.globalVolume = 64;
503 }
504 else if (x)
505 {
506 effOut = 0x11;
507 opOut = x;
508 workBuffers.globalVolume-=x*(workBuffers.speed-1);
509 if (workBuffers.globalVolume < 0)
510 workBuffers.globalVolume = 0;
511 }
512
513 }
514 else
515 {
516 // SUCKS
517 if (!(x==0x0F && !y) && !(y==0x0F && !x))
518 {
519 if (x==0x0F)
520 {
521 workBuffers.globalVolume += y;
522 if (workBuffers.globalVolume > 64)
523 workBuffers.globalVolume = 64;
524 effOut = 0x10;
525 opOut = (mp_ubyte)workBuffers.globalVolume;
526 }
527 else if (y==0x0F)
528 {
529 workBuffers.globalVolume -= x;
530 if (workBuffers.globalVolume < 0)
531 workBuffers.globalVolume = 0;
532 effOut = 0x10;
533 opOut = (mp_ubyte)workBuffers.globalVolume;
534 }
535 }
536 }
537 }
538 }
539 // set digibooster real BPM
540 else if (effIn == 0x52)
541 {
542 // store new BPM base
543 workBuffers.baseBpm = opIn;
544 // if it's not the default value recalculate BPM
545 if (workBuffers.baseBpm != 125)
546 {
547 // see digibooster pro player source
548 mp_sint32 realCiaTempo = (workBuffers.bpm * (workBuffers.baseBpm << 8) / 125) >> 8;
549 // clip if necessary
550 if (realCiaTempo > 255)
551 realCiaTempo = 255;
552 if (realCiaTempo < 32)
553 realCiaTempo = 32;
554 // write new command: 0xF with new operand
555 effOut = 0x0F;
556 opOut = realCiaTempo;
557 }
558 }
559 // FAR/669 (effects are really different, conversion doesn't make much sense)
560 else if (effIn == 0x70)
561 {
562 switch (opIn>>4)
563 {
564 case 0x0f:
565 effOut = 0xf;
566 opOut = opIn&0xf;
567 break;
568 }
569 }
570 else if (effIn)
571 {
572 #ifdef VERBOSE
573 printf("Missing effect %i, %i\n", effIn, opIn);
574 #endif
575 }
576 }
577
578 // convert FT2 compatible effect to volumn column effect if possible
convertToVolume(mp_ubyte eff,mp_ubyte op)579 static mp_ubyte convertToVolume(mp_ubyte eff, mp_ubyte op)
580 {
581
582 mp_ubyte vol = 0;
583
584 /*if (eff && eff != 0x0C)
585 {
586 printf("%i, %i\n", eff, op);
587 }*/
588
589 // set volume
590 if (eff == 0x0C)
591 {
592 vol = 0x10 + op;
593 }
594 // volslide
595 else if (eff == 0x0A)
596 {
597
598 // use last operand?
599 if (!op)
600 {
601 vol = 0x60;
602 }
603 // volslide down
604 else if (op & 0xF)
605 {
606 vol = 0x60 + (op&0xF);
607 }
608 // volslide up
609 else if (op >> 4)
610 {
611 vol = 0x70 + (op>>4);
612 }
613
614 }
615 // extra fine volslide up
616 else if (eff == 0xE && ((op>>4)==0xA))
617 {
618 vol = 0x90 + (op & 0xF);
619 }
620 // extra fine volslide down
621 else if (eff == 0xE && ((op>>4)==0xB))
622 {
623 vol = 0x80 + (op & 0xF);
624 }
625 // extra vibrato
626 else if (eff == 0x4)
627 {
628 if ((op>>4) && !(op&0xF))
629 {
630 vol = 0xA0 + (op>>4);
631 }
632 else if (!(op>>4)/* && (op&0xF)*/)
633 {
634 vol = 0xB0 + op;
635 }
636 }
637 // set panning
638 else if (eff == 0x8)
639 {
640 vol = 0xC0 + (op>>4);
641 }
642 // panning slide
643 else if (eff == 0x19)
644 {
645 // use last operand?
646 if (!op)
647 {
648 vol = 0xD0;
649 }
650 // panning slide left
651 else if (op & 0xF)
652 {
653 vol = 0xD0 + (op&0xF);
654 }
655 // panning slide right
656 else if (op >> 4)
657 {
658 vol = 0xE0 + (op>>4);
659 }
660 }
661 // porta to note
662 else if (eff == 0x03)
663 {
664 vol = 0xF0 + (op>>4);
665 }
666
667 return vol;
668 }
669
670 // Convert a bunch of effects (srcSlot with numEffects)
671 // into volume, eff and op (XM operands)
672 // curChan is the current channel
673 // workBuffers holds the last effect operands while processing
674 // effectBuffer holds a bunch (numEffectsInBuffer) of remaining
675 // effects from the last columns which might be allowed to go
676 // into another channel
677 // swapBuffer must be at least of effectBuffer size
convertEffects(mp_ubyte * srcSlot,mp_sint32 numEffects,mp_ubyte & volume,mp_ubyte & eff,mp_ubyte & op,mp_sint32 curChan,TWorkBuffers & workBuffers,mp_ubyte * effectBuffer,mp_sint32 & numEffectsInBuffer,mp_ubyte * swapBuffer,bool convertITTempoSlides)678 static void convertEffects(mp_ubyte* srcSlot,
679 mp_sint32 numEffects,
680 mp_ubyte& volume,
681 mp_ubyte& eff,
682 mp_ubyte& op,
683 mp_sint32 curChan,
684 TWorkBuffers& workBuffers,
685 mp_ubyte* effectBuffer,
686 mp_sint32& numEffectsInBuffer,
687 mp_ubyte* swapBuffer,
688 bool convertITTempoSlides)
689 {
690 mp_sint32 i;
691
692 // If there is only one effect this goes into the effect column
693 if (numEffects == 1)
694 {
695 volume = 0;
696
697 convertEffect(srcSlot[2], srcSlot[3], eff, op, curChan, workBuffers, convertITTempoSlides);
698 }
699 else if (numEffects >= 2)
700 {
701 mp_sint32 oldNum = numEffects;
702 numEffects+=numEffectsInBuffer;
703 mp_ubyte* effects = swapBuffer;
704
705 // Convert effects to be FT2 compatible
706 // Result will be written in effects
707 for (i = 0; i < oldNum; i++)
708 {
709 effects[i*2] = effects[i*2+1] = 0;
710 convertEffect(srcSlot[2+i*2], srcSlot[2+i*2+1], effects[i*2], effects[i*2+1], curChan, workBuffers, convertITTempoSlides);
711 }
712 // Append replacable effects which are left from other columns
713 for (i = 0; i < numEffectsInBuffer; i++)
714 {
715 mp_uint32 j = oldNum + i;
716 effects[j*2] = effectBuffer[i*2];
717 effects[j*2+1] = effectBuffer[i*2+1];
718 }
719
720 // Now "effects" contains all effects+operands
721 // We try to find a home for them
722 for (i = 0; i < numEffects; i++)
723 {
724 // could be MDL portamento + volslide
725 if (numEffects >= 3 &&
726 effects[i*2] == 0x3 &&
727 effects[i*2+1] == 0)
728 {
729 for (mp_sint32 j = 0; j < numEffects; j++)
730 {
731 if (effects[j*2] == 0xA &&
732 effects[j*2+1] != 0)
733 {
734 effects[i*2] = 0x5;
735 effects[i*2+1] = effects[j*2+1];
736
737 // clear out
738 effects[j*2] = 0;
739 effects[j*2+1] = 0;
740 }
741 }
742 }
743 // could be MDL vibrato + volslide
744 if (numEffects >= 3 &&
745 effects[i*2] == 0x4 &&
746 effects[i*2+1] == 0)
747 {
748 for (mp_sint32 j = 0; j < numEffects; j++)
749 {
750 if (effects[j*2] == 0xA &&
751 effects[j*2+1] != 0)
752 {
753 effects[i*2] = 0x6;
754 effects[i*2+1] = effects[j*2+1];
755
756 // clear out
757 effects[j*2] = 0;
758 effects[j*2+1] = 0;
759 }
760 }
761 }
762 }
763
764 volume = 0;
765 eff = 0;
766 op = 0;
767
768 for (i = 0; i < numEffects; i++)
769 {
770 // Effect nr. 1 => try to stuff into volume column first
771 if (i == 0)
772 {
773 // If this is a portamento to note command and it's
774 // volume column compatible we'll place it in the
775 // volume column
776 bool notePortaNotVolumeCompatible = ((effects[i*2] == 0x03) && (effects[i*2+1]&0xF));
777
778 if (convertToVolume(effects[i*2], effects[i*2+1]) && !volume && !notePortaNotVolumeCompatible)
779 {
780 volume = convertToVolume(effects[i*2], effects[i*2+1]);
781 // clear out
782 effects[i*2] = effects[i*2+1] = 0;
783 }
784 // didn't work
785 else if ((effects[i*2] || effects[i*2+1]) && (!eff && !op))
786 {
787 eff = effects[i*2];
788 op = effects[i*2+1];
789 // clear out
790 effects[i*2] = effects[i*2+1] = 0;
791 }
792 }
793 // for the rest of the effects, try to find
794 // free space, take effect column first, volume column secondly
795 else
796 {
797 if ((effects[i*2] || effects[i*2+1]) && (!eff && !op))
798 {
799 eff = effects[i*2];
800 op = effects[i*2+1];
801 // clear out
802 effects[i*2] = effects[i*2+1] = 0;
803 }
804 else if (convertToVolume(effects[i*2], effects[i*2+1]) && !volume)
805 {
806 volume = convertToVolume(effects[i*2], effects[i*2+1]);
807 // clear out
808 effects[i*2] = effects[i*2+1] = 0;
809 }
810 }
811
812 }
813
814 mp_uint32 numOutEffs = 0;
815 // Scan what's left and sort out effects which are not allowed to go into another channel
816 for (i = 0; i < numEffects; i++)
817 {
818 switch (effects[i*2])
819 {
820 case 0x0B:
821 case 0x0D:
822
823 case 0x0E:
824 switch (effects[i*2+1] >> 4)
825 {
826 case 0xE:
827 goto takeEffect;
828 }
829 break;
830
831 case 0x0F:
832 takeEffect:
833 effectBuffer[numOutEffs*2] = effects[i*2];
834 effectBuffer[numOutEffs*2+1] = effects[i*2+1];
835 numOutEffs++;
836 break;
837 }
838 }
839
840 numEffectsInBuffer = numOutEffs;
841
842 //if (numOutEffs)
843 // printf("%i\n", numOutEffs);
844
845 }
846
847 }
848
fillWorkBuffers(const XModule * module,mp_uint32 orderListIndex,TWorkBuffers & workBuffers)849 static void fillWorkBuffers(const XModule* module, mp_uint32 orderListIndex, TWorkBuffers& workBuffers)
850 {
851 workBuffers.clearBuffers();
852
853 mp_ubyte* lastArpeggio = workBuffers.lastArpeggio;
854 mp_ubyte* lastVolSlide = workBuffers.lastVolSlide;
855 mp_ubyte* lastGVolSlide = workBuffers.lastGVolSlide;
856 mp_ubyte* lastPorta = workBuffers.lastPorta;
857 mp_ubyte* lastNote = workBuffers.lastNote;
858 mp_ubyte* lastIns = workBuffers.lastIns;
859 mp_ubyte* lastTempoSlide = workBuffers.lastTempoSlide;
860
861 for (mp_uint32 orderIndex = 0; orderIndex < orderListIndex; orderIndex++)
862 {
863 mp_uint32 patIndex = module->header.ord[orderIndex];
864
865 TXMPattern* pattern = &module->phead[patIndex];
866
867 mp_sint32 slotSize = pattern->effnum*2+2;
868 mp_sint32 channum = pattern->channum, effnum = pattern->effnum;
869
870 mp_ubyte* srcSlot = pattern->patternData;
871
872 for (mp_sint32 rows = 0; rows < pattern->rows; rows++)
873 {
874 for (mp_sint32 c = 0; c < channum; c++)
875 {
876 if (srcSlot[0])
877 lastNote[c] = srcSlot[0];
878
879 if (srcSlot[1])
880 lastIns[c] = srcSlot[1];
881
882 // ----------- store last operands for S3M/MDL/DBM effects ---------------
883 const mp_ubyte* effSlot = srcSlot+2;
884 for (mp_sint32 effCnt = 0; effCnt < effnum; effCnt++)
885 {
886 mp_ubyte effIn = *effSlot;
887 mp_ubyte opIn = *(effSlot+1);
888
889 effSlot+=2;
890
891 if (effIn < 0x0f || effIn > 0x5a)
892 continue;
893
894 switch (effIn)
895 {
896 case 0x0f:
897 if (opIn >= 32)
898 workBuffers.bpm = opIn;
899 break;
900 case 0x10:
901 workBuffers.globalVolume = (mp_ubyte)(((mp_sint32)opIn*64)/255);
902 break;
903 case 0x16:
904 if (opIn >= 32)
905 {
906 workBuffers.bpm = opIn;
907 }
908 else
909 {
910 if (opIn) lastTempoSlide[c] = opIn;
911
912 if (lastTempoSlide[c])
913 {
914 mp_ubyte y = lastTempoSlide[c]>>4;
915 mp_ubyte x = lastTempoSlide[c]&0xf;
916
917 // tempo slide up
918 if (y)
919 {
920 workBuffers.bpm+=x*(workBuffers.speed-1);
921 if (workBuffers.bpm > 255)
922 workBuffers.bpm = 255;
923 }
924 // tempo slide down
925 else
926 {
927 workBuffers.bpm-=x*(workBuffers.speed-1);
928 if (workBuffers.bpm < 32)
929 workBuffers.bpm = 32;
930 }
931 }
932 }
933 break;
934 case 0x1C:
935 if (opIn)
936 workBuffers.speed = opIn;
937 break;
938 case 0x20:
939 if (!opIn) break;
940 lastArpeggio[c] = opIn;
941 break;
942 case 0x43:
943 if (!opIn) break;
944 lastPorta[c] = opIn;
945 break;
946 case 0x44:
947 if (!opIn) break;
948 lastPorta[c] = opIn;
949 break;
950 case 0x45:
951 if (!opIn) break;
952 lastVolSlide[c] = opIn;
953 break;
954 case 0x46:
955 if (!opIn) break;
956 lastVolSlide[c] = opIn;
957 break;
958 case 0x47:
959 if (!opIn) break;
960 lastPorta[c] = opIn;
961 break;
962 case 0x48:
963 if (!opIn) break;
964 lastPorta[c] = opIn;
965 break;
966 case 0x49:
967 if (!opIn) break;
968 lastVolSlide[c] = opIn;
969 break;
970 case 0x52:
971 workBuffers.baseBpm = opIn;
972 break;
973
974 case 0x59:
975 {
976 if (opIn)
977 lastGVolSlide[c] = opIn;
978
979 if (lastGVolSlide[c])
980 {
981 mp_ubyte y = lastGVolSlide[c]>>4;
982 mp_ubyte x = lastGVolSlide[c]&0xf;
983
984 if (!(x == 0xF && y)&&!(y == 0xF && x))
985 {
986 if (x && y) x = 0;
987
988 if (y)
989 {
990 workBuffers.globalVolume+=y*(workBuffers.speed-1);
991 if (workBuffers.globalVolume > 64)
992 workBuffers.globalVolume = 64;
993 }
994 else if (x)
995 {
996 workBuffers.globalVolume-=x*(workBuffers.speed-1);
997 if (workBuffers.globalVolume < 0)
998 workBuffers.globalVolume = 0;
999 }
1000
1001 }
1002 else
1003 {
1004 // SUCKS
1005 if (!(x==0x0F && !y) && !(y==0x0F && !x))
1006 {
1007 if (x==0x0F)
1008 {
1009 workBuffers.globalVolume += y;
1010 if (workBuffers.globalVolume > 64)
1011 workBuffers.globalVolume = 64;
1012 }
1013 else if (y==0x0F)
1014 {
1015 workBuffers.globalVolume -= x;
1016 if (workBuffers.globalVolume < 0)
1017 workBuffers.globalVolume = 0;
1018 }
1019 }
1020 }
1021 }
1022 break;
1023 }
1024 }
1025 }
1026
1027 srcSlot+=slotSize;
1028
1029 }
1030 }
1031 }
1032 }
1033
convertPattern(const XModule * module,const TXMPattern * srcPattern,mp_ubyte * dstPattern,mp_sint32 numChannels,TWorkBuffers & workBuffers,bool verbose)1034 static mp_sint32 convertPattern(const XModule* module, const TXMPattern* srcPattern, mp_ubyte* dstPattern, mp_sint32 numChannels, TWorkBuffers& workBuffers, bool verbose)
1035 {
1036
1037 bool convertITTempoSlides = module ? (module->getType() == XModule::ModuleType_IT) : false;
1038 bool newInsPTFlag = module ? ((module->header.flags & XModule::MODULE_PTNEWINSTRUMENT) != 0) : false;
1039 bool newInsST3Flag = module ? ((module->header.flags & XModule::MODULE_ST3NEWINSTRUMENT) != 0) : false;
1040
1041 if (module)
1042 {
1043 mp_sint32 patNum = -1;
1044 for (mp_uint32 i = 0; i < module->header.patnum; i++)
1045 {
1046 if (srcPattern == &module->phead[i])
1047 {
1048 patNum = i;
1049 break;
1050 }
1051 }
1052
1053 if (patNum != -1)
1054 {
1055 mp_sint32 orderListPos = -1;
1056 for (mp_uint32 i = 0; i < module->header.ordnum; i++)
1057 {
1058 if (module->header.ord[i] == patNum)
1059 {
1060 orderListPos = i;
1061 break;
1062 }
1063 }
1064
1065 if (orderListPos != -1)
1066 {
1067 fillWorkBuffers(module, orderListPos, workBuffers);
1068 }
1069 }
1070 }
1071
1072 mp_ubyte* lastNote = workBuffers.lastNote;
1073 mp_ubyte* lastIns = workBuffers.lastIns;
1074
1075 #ifdef MILKYTRACKER
1076 newInsPTFlag = false;
1077 #endif
1078
1079 mp_ubyte* effectBuffer = new mp_ubyte[srcPattern->rows * numChannels * srcPattern->effnum];
1080 mp_ubyte* swapBuffer = new mp_ubyte[srcPattern->rows * numChannels * srcPattern->effnum];
1081 mp_sint32 numEffectsInBuffer = 0;
1082
1083 for (mp_sint32 rows = 0; rows < srcPattern->rows; rows++)
1084 {
1085 bool correctPLMFarJump = false;
1086 mp_uint32 correctPLMFarJumpChannel = 0;
1087 mp_sint32 PLMFarJumpPos = 0;
1088 mp_sint32 PLMFarJumpRow = 0;
1089
1090 mp_sint32 c;
1091
1092 for (c = 0; c < srcPattern->channum; c++)
1093 {
1094 if (c < numChannels)
1095 {
1096 mp_ubyte* dstSlot = dstPattern+(rows*(numChannels*5) + (c*5));
1097 mp_ubyte* srcSlot = srcPattern->patternData+(rows*(srcPattern->channum*(srcPattern->effnum*2+2)) + c*(srcPattern->effnum*2+2));
1098
1099 if (srcSlot[0])
1100 lastNote[c] = srcSlot[0];
1101
1102 if (srcSlot[1])
1103 {
1104 lastIns[c] = srcSlot[1];
1105 //if (srcSlot[0])
1106 // lastIns2[c] = srcSlot[1];
1107 }
1108
1109 mp_sint32 srcNote = (mp_sint32)srcSlot[0];
1110
1111 if (lastIns[c] && srcNote > 0 && srcNote <= XModule::NOTE_LAST)
1112 {
1113 srcNote+=workBuffers.noteRangeRemapper[lastIns[c]-1];
1114 if (srcNote > XModule::NOTE_LAST || srcNote < 0)
1115 srcNote = 0;
1116 }
1117 if (srcNote == XModule::NOTE_OFF || srcNote == XModule::NOTE_CUT) srcNote = 97;
1118 else if (srcNote > 96) srcNote = 0;
1119
1120 dstSlot[0] = srcNote;
1121
1122 // instrument
1123 dstSlot[1] = srcSlot[1];
1124
1125 convertEffects(srcSlot,
1126 srcPattern->effnum,
1127 dstSlot[2],
1128 dstSlot[3],
1129 dstSlot[4],
1130 c,
1131 workBuffers,
1132 effectBuffer,
1133 numEffectsInBuffer,
1134 swapBuffer,
1135 convertITTempoSlides);
1136
1137 // try to find workaround for PLM far-jump
1138 if (module && module->getType() == XModule::ModuleType_PLM && dstSlot[3] == 0x0B)
1139 {
1140 mp_ubyte* eff = srcSlot+2;
1141 for (mp_sint32 i = 0; i < srcPattern->effnum; i++)
1142 {
1143 if (eff[i*2] == 0x2B)
1144 {
1145 PLMFarJumpPos = eff[i*2+1];
1146 PLMFarJumpRow = eff[((i+1)%srcPattern->effnum)*2+1];
1147 correctPLMFarJump = true;
1148 correctPLMFarJumpChannel = c;
1149 break;
1150 }
1151 }
1152 }
1153
1154 // * some nasty protracker style fixes
1155 // * trying to emulate protracker 3.15 behaviour with FT2 methods
1156 mp_sint32 i = srcSlot[1];
1157 if (module && i && newInsPTFlag)
1158 {
1159 if (!dstSlot[0])
1160 {
1161 if (module->instr[i-1].samp == 0 ||
1162 module->instr[i-1].snum[0] == -1)
1163 {
1164 dstSlot[0] = 97;
1165 }
1166 else
1167 {
1168 mp_sint32 s = module->instr[i-1].snum[0];
1169 if (s != -1 && !dstSlot[2])
1170 dstSlot[2] = (mp_ubyte)(((mp_sint32)module->smp[s].vol*64)/255)+0x10;
1171 }
1172 }
1173 else
1174 {
1175 if (module->instr[i-1].samp == 0 ||
1176 module->instr[i-1].snum[dstSlot[0]] == -1)
1177 {
1178 dstSlot[0] = 97;
1179 }
1180 }
1181
1182 }
1183 else if (module && i && newInsST3Flag)
1184 {
1185 if (!dstSlot[0])
1186 {
1187 if (!(module->instr[i-1].samp == 0 ||
1188 module->instr[i-1].snum[0] == -1))
1189 {
1190 mp_sint32 s = module->instr[i-1].snum[0];
1191 if (s != -1 && !dstSlot[2])
1192 dstSlot[2] = (mp_ubyte)(((mp_sint32)module->smp[s].vol*64)/255)+0x10;
1193 }
1194 else
1195 {
1196 dstSlot[0] = 0;
1197 }
1198 }
1199 }
1200
1201 // * trying to emulate MTM behaviour
1202 // * Sample offset command triggers last note again
1203 if (module && module->getType() == XModule::ModuleType_MTM && !dstSlot[0] && dstSlot[3] == 0x9)
1204 dstSlot[0] = lastNote[c];
1205
1206 }
1207
1208 if (correctPLMFarJump)
1209 {
1210 for (c = 0; c < srcPattern->channum; c++)
1211 {
1212 mp_ubyte* dstSlot = dstPattern+(rows*(numChannels*5) + (c*5));
1213
1214 if (dstSlot[3] == 0)
1215 {
1216 if (c > (signed)correctPLMFarJumpChannel)
1217 {
1218 dstSlot[3] = 0x0D;
1219 dstSlot[4] = (PLMFarJumpRow/10)*16+(PLMFarJumpRow%10);
1220 }
1221 else
1222 {
1223 mp_ubyte* srcSlot = dstPattern+(rows*(numChannels*5) + (correctPLMFarJumpChannel*5));
1224
1225 dstSlot[3] = srcSlot[3];
1226 dstSlot[4] = srcSlot[4];
1227
1228 srcSlot[3] = 0x0D;
1229 srcSlot[4] = (PLMFarJumpRow/10)*16+(PLMFarJumpRow%10);
1230 }
1231 break;
1232 }
1233 }
1234 }
1235
1236 }
1237
1238 }
1239
1240 delete[] swapBuffer;
1241 delete[] effectBuffer;
1242
1243 return MP_OK;
1244 }
1245
packPattern(const mp_ubyte * pattern,mp_ubyte * outputPattern,mp_sint32 numRows,mp_sint32 numChannels)1246 mp_sint32 packPattern(const mp_ubyte* pattern, mp_ubyte* outputPattern, mp_sint32 numRows, mp_sint32 numChannels)
1247 {
1248 mp_sint32 i,j,z,b1,x,y;
1249 mp_ubyte pack[6];
1250
1251 /*i = numRows*numChannels*5 - 1;
1252 while (i > 0 && !pattern[i])
1253 i--;
1254 mp_sint32 max = i;
1255
1256 printf("%i, %i\n", numRows*numChannels*5, max); */
1257
1258 // -------------------------
1259 // pack pattern (xm packing)
1260 // -------------------------
1261 j = z = b1 = 0;
1262 for (x=0; x < numRows; x++)
1263 for (y=0; y < numChannels; y++)
1264 {
1265 //if (z > max)
1266 // goto finishedPacking;
1267
1268 memset(&pack,0,6);
1269 i=0;
1270 if (pattern[z])
1271 {
1272 b1=1;
1273 pack[0]|=1;
1274 pack[i+1]=pattern[z];
1275 i++;
1276 }
1277 if (pattern[z+1])
1278 {
1279 b1=1;
1280 pack[0]|=2;
1281 pack[i+1]=pattern[z+1];
1282 i++;
1283 }
1284 if (pattern[z+2])
1285 {
1286 b1=1;
1287 pack[0]|=4;
1288 pack[i+1]=pattern[z+2];
1289 i++;
1290 }
1291 if (pattern[z+3])
1292 {
1293 b1=1;
1294 pack[0]|=8;
1295 pack[i+1]=pattern[z+3];
1296 i++;
1297 }
1298 if (pattern[z+4])
1299 {
1300 b1=1;
1301 pack[0]|=16;
1302 pack[i+1]=pattern[z+4];
1303 i++;
1304 }
1305 if (i<5)
1306 {
1307 pack[0]|=128;
1308 memcpy(outputPattern+j,&pack,i+1);
1309 j+=i+1;
1310 }
1311 else
1312 {
1313 memcpy(outputPattern+j,pattern+z,5);
1314 j+=5;
1315 }
1316
1317 z+=5;
1318
1319 }
1320
1321 //finishedPacking:
1322 return j;
1323 }
1324
sort(mp_sword * array,mp_sint32 l,mp_sint32 r)1325 static void sort(mp_sword* array,mp_sint32 l, mp_sint32 r)
1326 {
1327 mp_sint32 i,j;
1328 mp_sword x,y;
1329 i=l; j=r; x=array[(l+r)/2];
1330 do
1331 {
1332 while (array[i]<x) i++;
1333 while (x<array[j]) j--;
1334 if (i<=j)
1335 {
1336 y=array[i]; array[i]=array[j]; array[j]=y;
1337 i++; j--;
1338 }
1339 } while (i<=j);
1340 if (l<j) sort(array,l,j);
1341 if (i<r) sort(array,i,r);
1342 }
1343
saveExtendedModule(const SYSCHAR * fileName)1344 mp_sint32 XModule::saveExtendedModule(const SYSCHAR* fileName)
1345 {
1346 mp_sint32 i,j,k,l;
1347
1348 TWorkBuffers workBuffers;
1349 workBuffers.bpm = header.speed;
1350 workBuffers.speed = header.tempo;
1351
1352 // ------ prerequisites ---------------------------------
1353
1354 // step one, find last used pattern
1355 mp_sint32 patNum = getNumUsedPatterns();
1356 if (!patNum)
1357 patNum++;
1358
1359 // step two, find last used instrument
1360 mp_sint32 insNum = getNumUsedInstruments();
1361 if (!insNum)
1362 insNum++;
1363
1364 // ------ start ---------------------------------
1365 XMFile f(fileName, true);
1366
1367 if (!f.isOpenForWriting())
1368 return MP_DEVICE_ERROR;
1369
1370 f.write("Extended Module: ",1,17);
1371
1372 char titleBuffer[MP_MAXTEXT+1], titleBufferTemp[MP_MAXTEXT+1];
1373 memset(titleBuffer, 0, sizeof(titleBuffer));
1374 memset(titleBufferTemp, 0, sizeof(titleBufferTemp));
1375 convertStr(reinterpret_cast<char*>(titleBuffer), reinterpret_cast<char*>(header.name), MP_MAXTEXT);
1376 if (strlen(titleBuffer) > 20)
1377 {
1378 mp_sint32 i = 0;
1379 mp_sint32 len = (mp_sint32)strlen(titleBuffer);
1380 while (titleBuffer[i] <= ' ' && i < len)
1381 i++;
1382
1383 memcpy(titleBufferTemp, titleBuffer+i, strlen(titleBuffer) - i);
1384 f.write(titleBufferTemp, 1, 20);
1385 }
1386 else
1387 f.write(header.name, 1, 20);
1388 header.whythis1a = 0x1a;
1389 f.writeByte(header.whythis1a);
1390 #ifdef MILKYTRACKER
1391 #include "../tracker/version.h"
1392 f.write(MILKYTRACKER_VERSION_STRING, 1, 20);
1393 #else
1394 f.write(header.tracker, 1, 20);
1395 #endif
1396 header.ver = 0x104;
1397 f.writeWord(header.ver);
1398 header.hdrsize = 276;
1399 f.writeDword(header.hdrsize);
1400 f.writeWord(header.ordnum);
1401 f.writeWord(header.restart);
1402
1403 mp_uword numChannels = header.channum&1 ? header.channum+1 : header.channum;
1404
1405 f.writeWord(numChannels);
1406 f.writeWord(patNum);
1407 f.writeWord(insNum);
1408 f.writeWord(header.freqtab);
1409 f.writeWord(header.tempo);
1410 f.writeWord(header.speed);
1411 f.write(header.ord,1,256);
1412
1413 mp_ubyte lowerNoteBound[256];
1414 mp_ubyte upperNoteBound[256];
1415
1416 for (i = 0; i < 256; i++)
1417 {
1418 lowerNoteBound[i] = XModule::NOTE_LAST;
1419 upperNoteBound[i] = 0;
1420 }
1421
1422 mp_ubyte* lastIns = new mp_ubyte[header.channum];
1423 memset(lastIns, 0, header.channum);
1424
1425 for (i = 0; i < patNum; i++)
1426 {
1427 TXMPattern* srcPattern = &phead[i];
1428
1429 mp_sint32 channum = srcPattern->channum >= header.channum ? header.channum : srcPattern->channum;
1430
1431 for (mp_sint32 rows = 0; rows < srcPattern->rows; rows++)
1432 for (mp_sint32 c = 0; c < channum; c++)
1433 {
1434
1435 mp_ubyte* srcSlot = srcPattern->patternData+(rows*(srcPattern->channum*(srcPattern->effnum*2+2)) + c*(srcPattern->effnum*2+2));
1436
1437 if (srcSlot[1])
1438 lastIns[c] = srcSlot[1];
1439
1440 if (lastIns[c] && srcSlot[0] && srcSlot[0] < XModule::NOTE_OFF)
1441 {
1442 if (srcSlot[0] > upperNoteBound[lastIns[c]-1]) upperNoteBound[lastIns[c]-1] = srcSlot[0];
1443 if (srcSlot[0] < lowerNoteBound[lastIns[c]-1]) lowerNoteBound[lastIns[c]-1] = srcSlot[0];
1444 }
1445 }
1446
1447 }
1448
1449 delete[] lastIns;
1450
1451 for (i = 0; i < insNum; i++)
1452 {
1453 mp_sint32 remapper = 0;
1454 if (upperNoteBound[i] > 96)
1455 remapper = upperNoteBound[i] - 96;
1456
1457 if (remapper < lowerNoteBound[i])
1458 workBuffers.noteRangeRemapper[i] = -remapper;
1459
1460 #ifdef VERBOSE
1461 printf("%i - %i (%i)\n", lowerNoteBound[i], upperNoteBound[i], workBuffers.noteRangeRemapper[i]);
1462 #endif
1463 }
1464
1465 for (i = 0; i < patNum; i++)
1466 {
1467 mp_sint32 numRows = phead[i].rows;
1468
1469 if (numRows == 0)
1470 numRows = 1;
1471
1472 mp_sint32 patChNum = (phead[i].channum+(phead[i].channum&1));
1473
1474 if (patChNum < numChannels)
1475 patChNum = numChannels;
1476
1477 mp_sint32 len = numRows * patChNum * 5;
1478
1479 mp_ubyte* srcPattern = new mp_ubyte[len];
1480 mp_ubyte* dstPattern = new mp_ubyte[len];
1481
1482 memset(srcPattern, 0, len);
1483 memset(dstPattern, 0, len);
1484
1485 convertPattern(this, &phead[i], srcPattern, numChannels, workBuffers, false);
1486
1487 len = packPattern(srcPattern, dstPattern, numRows, numChannels);
1488
1489 #ifdef VERBOSE
1490 printf("Uncompressed pattern size: %i, compressed: %i\n", numRows * numChannels * 5, len);
1491 #endif
1492
1493 f.writeDword(9);
1494 f.writeByte(0);
1495 f.writeWord(numRows);
1496 f.writeWord(len);
1497
1498 f.write(dstPattern, 1, len);
1499
1500 //printf("Packed Size: %i\n", len);
1501
1502 delete[] srcPattern;
1503 delete[] dstPattern;
1504 }
1505
1506 for (i = 0; i < insNum; i++)
1507 {
1508 //mp_sint32 maxSample = instr[i].samp - 1;
1509 if (instr[i].samp > 0)
1510 {
1511 mp_sword usedSamples[256];
1512 memset(usedSamples, 0, sizeof(usedSamples));
1513 mp_sint32 numUsedSamples = 0;
1514
1515 #ifdef MILKYTRACKER
1516 if (type == ModuleType_XM && header.smpnum == header.insnum*16)
1517 {
1518 // save all samples within an instrument rather than the
1519 // used ones (referenced by note mapping)
1520 for (j = 0; j < 16; j++)
1521 usedSamples[j] = i*16+j;
1522
1523 numUsedSamples = 0;
1524
1525 // find last used sample in instrument
1526 for (j = 15; j >= 0; j--)
1527 {
1528 mp_sint32 index = usedSamples[j];
1529 char buffer[MP_MAXTEXT+1];
1530 convertStr(buffer, reinterpret_cast<char*>(smp[index].name), MP_MAXTEXT);
1531 if (strlen(buffer) || smp[index].samplen)
1532 {
1533 numUsedSamples = j+1;
1534 break;
1535 }
1536 }
1537 }
1538 else
1539 #endif
1540 {
1541 // find referenced samples in instrument and save those
1542 for (j = 0; j < 96; j++)
1543 {
1544 mp_sword s = instr[i].snum[j];
1545
1546 if (s == -1)
1547 continue;
1548
1549 bool used = false;
1550 for (k = 0; k < numUsedSamples; k++)
1551 {
1552 if (usedSamples[k] == s)
1553 {
1554 used = true;
1555 break;
1556 }
1557 }
1558 if (!used && smp[s].sample && smp[s].samplen)
1559 usedSamples[numUsedSamples++] = s;
1560 }
1561
1562 sort(usedSamples, 0, numUsedSamples-1);
1563 }
1564
1565 f.writeDword(numUsedSamples > 0 ? 263 : 29);
1566 f.write(&instr[i].name,1,22);
1567 f.writeByte(0);
1568 f.writeWord(numUsedSamples);
1569
1570 if (!numUsedSamples)
1571 continue;
1572
1573 f.writeDword(40);
1574
1575 mp_ubyte nbu[96];
1576
1577 for (j = 0; j < 96; j++)
1578 {
1579 mp_sword s = instr[i].snum[j];
1580
1581 for (k = 0; k < numUsedSamples; k++)
1582 if (usedSamples[k] == s)
1583 {
1584 nbu[j] = k;
1585 break;
1586 }
1587 }
1588
1589 f.write(nbu, 1, 96);
1590
1591 mp_sint32 venvIndex = -1;
1592 mp_sint32 penvIndex = -1;
1593
1594 for (j = 0; j < numUsedSamples; j++)
1595 {
1596 if (smp[usedSamples[j]].venvnum && venvIndex == -1)
1597 venvIndex = smp[usedSamples[j]].venvnum - 1;
1598
1599 if (smp[usedSamples[j]].penvnum && penvIndex == -1)
1600 penvIndex = smp[usedSamples[j]].penvnum - 1;
1601
1602 }
1603
1604 #ifdef VERBOSE
1605 printf("%i, %i\n", venvIndex, penvIndex);
1606 #endif
1607 mp_sint32 step = 0;
1608
1609 if (venvIndex >= 0 && venvIndex < header.volenvnum)
1610 {
1611 step = venvs[venvIndex].num > 12 ? venvs[venvIndex].num*256 / 12 : 256;
1612 l = 0;
1613 for (k = 0; k < 12; k++)
1614 {
1615 f.writeWord(venvs[venvIndex].env[l>>8][0]);
1616 f.writeWord(venvs[venvIndex].env[l>>8][1]>>2);
1617 l+=step;
1618 }
1619 }
1620 else
1621 {
1622 // emptyness
1623 for (k = 0; k < 12; k++)
1624 {
1625 f.writeWord(0);
1626 f.writeWord(0);
1627 }
1628 }
1629 if (penvIndex >= 0 && penvIndex < header.panenvnum)
1630 {
1631 step = penvs[penvIndex].num > 12 ? penvs[penvIndex].num*256 / 12 : 256;
1632 l = 0;
1633 for (k = 0; k < 12; k++)
1634 {
1635 f.writeWord(penvs[penvIndex].env[l>>8][0]);
1636 f.writeWord(penvs[penvIndex].env[l>>8][1]>>2);
1637 l+=step;
1638 }
1639 }
1640 else
1641 {
1642 // emptyness
1643 for (k = 0; k < 12; k++)
1644 {
1645 f.writeWord(0);
1646 f.writeWord(0);
1647 }
1648 }
1649
1650 if (venvIndex >= 0 && venvIndex < header.volenvnum)
1651 f.writeByte(venvs[venvIndex].num > 12 ? 12 : venvs[venvIndex].num); // number of volume points
1652 else
1653 f.writeByte(0); // number of volume points
1654
1655 if (penvIndex >= 0 && penvIndex < header.panenvnum)
1656 f.writeByte(penvs[penvIndex].num > 12 ? 12 : penvs[penvIndex].num); // number of panning points
1657 else
1658 f.writeByte(0); // number of panning points
1659
1660 if (venvIndex >= 0 && venvIndex < header.volenvnum)
1661 {
1662 f.writeByte(venvs[venvIndex].sustain); // volume sustain point
1663 f.writeByte(venvs[venvIndex].loops); // volume start point
1664 f.writeByte(venvs[venvIndex].loope); // volume end point
1665 }
1666 else
1667 {
1668 f.writeByte(0);
1669 f.writeByte(0);
1670 f.writeByte(0);
1671 }
1672
1673 if (penvIndex >= 0 && penvIndex < header.panenvnum)
1674 {
1675 f.writeByte(penvs[penvIndex].sustain); // panning sustain point
1676 f.writeByte(penvs[penvIndex].loops); // panning start point
1677 f.writeByte(penvs[penvIndex].loope); // panning end point
1678 }
1679 else
1680 {
1681 f.writeByte(0);
1682 f.writeByte(0);
1683 f.writeByte(0);
1684 }
1685
1686 if (venvIndex >= 0 && venvIndex < header.volenvnum)
1687 f.writeByte(venvs[venvIndex].type); // volume type
1688 else
1689 f.writeByte(0);
1690
1691 if (penvIndex >= 0 && penvIndex < header.panenvnum)
1692 f.writeByte(penvs[penvIndex].type); // panning type
1693 else
1694 f.writeByte(0);
1695
1696 // take rest of the instrument info from first sample in the instrument
1697 // will probably not work for exported .MDL songs.
1698 // solution might be to create one instrument for each
1699 // sample and remap instrument field in the patterns
1700 l = usedSamples[0];
1701
1702 f.writeByte(smp[l].vibtype); // vibrato type
1703 f.writeByte(smp[l].vibsweep); // vibrato sweep
1704 f.writeByte(smp[l].vibdepth>>1); // vibrato depth
1705 f.writeByte(smp[l].vibrate); // vibrato rate
1706
1707 if (instr[i].flags & TXMInstrument::IF_ITFADEOUT)
1708 f.writeWord(instr[i].volfade>>1); // volume fadeout
1709 else
1710 f.writeWord(smp[l].volfade>>1); // volume fadeout
1711
1712 f.writeWord(0); // reserved
1713
1714 mp_ubyte extra[20];
1715 memset(extra, 0, sizeof(extra));
1716 f.write(extra, 1, 20);
1717
1718 for (j = 0; j < numUsedSamples; j++)
1719 {
1720 k = usedSamples[j];
1721
1722 f.writeDword((smp[k].type&16) ? smp[k].samplen <<1 : smp[k].samplen);
1723 f.writeDword((smp[k].type&16) ? smp[k].loopstart << 1 : smp[k].loopstart);
1724 f.writeDword((smp[k].type&16) ? smp[k].looplen << 1 : smp[k].looplen);
1725
1726 mp_sint32 relnote = smp[k].relnote - workBuffers.noteRangeRemapper[i] + header.relnote;
1727 if (relnote<-96) relnote = -96;
1728 if (relnote>95) relnote = 95;
1729
1730 mp_sint32 finetune = smp[k].finetune;
1731
1732 // make some ULT linear finetune to finetune & relative note num approximation
1733 if (smp[k].freqadjust != 0)
1734 {
1735 mp_sint32 c4spd = getc4spd(relnote,finetune);
1736 c4spd+=((c4spd*smp[k].freqadjust)/32768);
1737
1738 mp_sbyte rn,ft;
1739 convertc4spd(c4spd, &ft, &rn);
1740 finetune = ft;
1741 relnote = rn;
1742 }
1743
1744 f.writeByte(smp[k].vol*64/255);
1745 f.writeByte((mp_sbyte)finetune);
1746
1747 mp_ubyte type = smp[k].type;
1748 // Only alowed bits 0,1 and 3
1749 type &= 3+16;
1750 // Bits 0 and 1 are not allowed to be set at the same time
1751 if ((type & 3) == 3) type &= ~1;
1752
1753 f.writeByte(type);
1754 f.writeByte((smp[k].flags & 2) ? smp[k].pan : 0x80);
1755
1756 f.writeByte((mp_sbyte)relnote);
1757 f.writeByte(0);
1758
1759 f.write(smp[k].name, 1, 22);
1760 }
1761
1762 for (j = 0; j < numUsedSamples; j++)
1763 {
1764 k = usedSamples[j];
1765
1766 if (!smp[k].sample)
1767 continue;
1768
1769 if (smp[k].type & 16)
1770 {
1771 mp_sword* packedSampleData = new mp_sword[smp[k].samplen];
1772
1773 mp_sword b1,b2,b3;
1774
1775 b1=0;
1776 for (mp_uint32 j = 0; j < smp[k].samplen; j++)
1777 {
1778 b3 = smp[k].getSampleValue(j);
1779 b2 = b3-b1;
1780 packedSampleData[j] = b2;
1781 b1 = b3;
1782 }
1783
1784 f.writeWords((mp_uword*)packedSampleData, smp[k].samplen);
1785
1786 delete[] packedSampleData;
1787
1788 }
1789 else
1790 {
1791 mp_sbyte* packedSampleData = new mp_sbyte[smp[k].samplen];
1792
1793 mp_sbyte b1,b2,b3;
1794
1795 b1=0;
1796 for (mp_uint32 j = 0; j < smp[k].samplen; j++)
1797 {
1798 b3 = smp[k].getSampleValue(j);
1799 b2 = b3-b1;
1800 packedSampleData[j] = b2;
1801 b1 = b3;
1802 }
1803
1804 f.write(packedSampleData, 1, smp[k].samplen);
1805
1806 delete[] packedSampleData;
1807 }
1808 }
1809 }
1810 else
1811 {
1812 f.writeDword(instr[i].samp > 0 ? 263 : 29);
1813 f.write(&instr[i].name,1,22);
1814 f.writeByte(0);
1815 f.writeWord(instr[i].samp);
1816 }
1817
1818 }
1819
1820
1821 return MP_OK;
1822 }
1823
swap(mp_uword x)1824 static mp_uword swap(mp_uword x)
1825 {
1826 return (x>>8)+((x&255)<<8);
1827 }
1828
prep(mp_sint32 v)1829 static mp_uword prep(mp_sint32 v)
1830 {
1831 const int MAXSIZE = 0x1ffff;
1832 if (v&1) v++;
1833 if (v > MAXSIZE)
1834 v = MAXSIZE;
1835
1836 return (mp_uword)(v >> 1);
1837 }
1838
saveProtrackerModule(const SYSCHAR * fileName)1839 mp_sint32 XModule::saveProtrackerModule(const SYSCHAR* fileName)
1840 {
1841 static const mp_sint32 periods[12] = {1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907};
1842
1843 static const mp_sint32 originalPeriods[] = {1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906,
1844 856,808,762,720,678,640,604,570,538,508,480,453,
1845 428,404,381,360,339,320,302,285,269,254,240,226,
1846 214,202,190,180,170,160,151,143,135,127,120,113,
1847 107,101,95,90,85,80,75,71,67,63,60,56};
1848
1849 TWorkBuffers workBuffers;
1850
1851 XMFile f(fileName, true);
1852
1853 if (!f.isOpenForWriting())
1854 return MP_DEVICE_ERROR;
1855
1856 f.write(header.name,1,20);
1857
1858 mp_sint32 i,j,k;
1859
1860 // - instruments -------------------------------------------
1861 for (i = 0; i < 31; i++)
1862 {
1863 f.write(instr[i].name, 1, 22);
1864
1865 // sample seems to be used
1866 if (instr[i].samp)
1867 {
1868 mp_sint32 s = -1;
1869 for (j = 0; j < 120; j++)
1870 if (instr[i].snum[j] >= 0)
1871 {
1872 s = instr[i].snum[j];
1873 break;
1874 }
1875
1876 if (s == -1)
1877 goto unused;
1878
1879 mp_sint32 fti = (mp_sint32)smp[s].finetune + 128;
1880 if (!(fti & 0xF) && !smp[s].relnote)
1881 {
1882 k = (((mp_uint32)(fti-128)) >> 4) & 0xF;
1883 }
1884 else
1885 {
1886 mp_sint32 c4spd = getc4spd(smp[s].relnote, smp[s].finetune);
1887
1888 mp_sint32 dc4 = abs(sfinetunes[0]-c4spd);
1889
1890 k = 0;
1891 for (j = 1; j < 16; j++)
1892 if (abs(sfinetunes[j]-c4spd) < dc4)
1893 {
1894 dc4 = abs(sfinetunes[j]-c4spd);
1895 k = j;
1896 }
1897 }
1898
1899 f.writeWord(swap(prep(smp[s].samplen)));
1900
1901 f.writeByte(k);
1902 f.writeByte((mp_ubyte)(((mp_sint32)smp[s].vol*64)/255));
1903
1904 if (smp[s].type & 3)
1905 {
1906 mp_sint32 loopend = /*smp[s].loopstart + */smp[s].looplen;
1907
1908 if (smp[s].type & 32)
1909 {
1910 f.writeWord(0);
1911 }
1912 else
1913 {
1914 if (!smp[s].loopstart && smp[s].looplen != smp[s].samplen)
1915 {
1916 f.writeWord(swap(1));
1917 }
1918 else if (!smp[s].loopstart && smp[s].looplen == smp[s].samplen)
1919 {
1920 f.writeWord(0);
1921 }
1922 else
1923 f.writeWord(swap(prep(smp[s].loopstart)));
1924 }
1925
1926 f.writeWord(swap(prep(loopend)));
1927 }
1928 else
1929 {
1930 f.writeWord(swap(0));
1931 f.writeWord(swap(1));
1932 }
1933
1934 }
1935 else
1936 {
1937 unused:
1938 f.writeWord(swap(0));
1939 f.writeByte(0);
1940 f.writeByte(0);
1941 f.writeWord(swap(0));
1942 f.writeWord(swap(1));
1943 }
1944
1945 }
1946
1947 // - orderlist -------------------------------------------
1948 f.writeByte((mp_ubyte)header.ordnum);
1949
1950 f.writeByte(127);
1951
1952 mp_ubyte ord[128];
1953
1954 memset(ord, 0, sizeof(ord));
1955
1956 j = 0;
1957 for (i = 0; i < 128; i++)
1958 {
1959 if (header.ord[i] < 254)
1960 ord[j++] = header.ord[i];
1961 else if (header.ord[i] == 255)
1962 break;
1963 }
1964
1965 f.write(ord, 1, 128);
1966
1967 mp_uword numChannels = header.channum&1 ? header.channum+1 : header.channum;
1968
1969 if (numChannels < 1 || numChannels > 99)
1970 return MP_UNSUPPORTED;
1971
1972 // - patterns -------------------------------------------
1973 mp_sint32 numPatterns = 0;
1974 for (i = 0; i < 128; i++)
1975 {
1976 if (ord[i] > numPatterns)
1977 numPatterns = ord[i];
1978 }
1979
1980 char modMagic[4];
1981 if(numChannels == 4)
1982 {
1983 // ProTracker may not load files with more than 64 patterns correctly if we do not specify the M!K! magic.
1984 if(numPatterns <= 63)
1985 memcpy(modMagic, "M.K.", 4);
1986 else
1987 memcpy(modMagic, "M!K!", 4);
1988 } else if(numChannels < 10)
1989 {
1990 memcpy(modMagic, "0CHN", 4);
1991 modMagic[0] += static_cast<char>(numChannels);
1992 } else
1993 {
1994 memcpy(modMagic, "00CH", 4);
1995 modMagic[0] += static_cast<char>(numChannels / 10u);
1996 modMagic[1] += static_cast<char>(numChannels % 10u);
1997 }
1998 f.write(modMagic, 1, 4);
1999
2000 for (i = 0; i < numPatterns+1; i++)
2001 {
2002 mp_sint32 numRows = phead[i].rows;
2003
2004 if (numRows == 0)
2005 numRows = 1;
2006
2007 mp_sint32 patChNum = (phead[i].channum+(phead[i].channum&1));
2008
2009 if (patChNum < numChannels)
2010 patChNum = numChannels;
2011
2012 mp_sint32 len = numRows * patChNum * 5;
2013 mp_sint32 lenDst = (numRows < 64 ? 64 : numRows) * patChNum * 4;
2014
2015 mp_ubyte* srcPattern = new mp_ubyte[len];
2016 mp_ubyte* dstPattern = new mp_ubyte[lenDst];
2017
2018 memset(srcPattern, 0, len);
2019 memset(dstPattern, 0, lenDst);
2020
2021 convertPattern(this, &phead[i], srcPattern, numChannels, workBuffers, false);
2022
2023 for (mp_sint32 r = 0; r < 64; r++)
2024 for (mp_sint32 c = 0; c < numChannels; c++)
2025 {
2026
2027 if (r < numRows)
2028 {
2029
2030 mp_sint32 srcIndex = (r*numChannels*5)+(c*5);
2031 mp_sint32 dstIndex = (r*numChannels*4)+(c*4);
2032
2033 mp_sint32 period = 0;
2034
2035 mp_ubyte note = srcPattern[srcIndex];
2036
2037 //note = r+24+1;
2038
2039 if (note)
2040 {
2041 note--;
2042 if (note >= 24 && note < 24+12*5)
2043 period = originalPeriods[note-24];
2044 else
2045 period = (periods[note%12]*16>>((note/12)))>>2;
2046 }
2047
2048 mp_ubyte ins = srcPattern[srcIndex+1];
2049
2050 if (ins > 31)
2051 ins = 0;
2052
2053 mp_ubyte eff = 0;
2054 mp_ubyte op = 0;
2055
2056 // First convert volume command to PT compatible effect again :)
2057 XModule::convertXMVolumeEffects(srcPattern[srcIndex+2], eff, op);
2058 mp_ubyte tmpEff = eff, tmpOp = op;
2059 convertEffect(tmpEff, tmpOp, eff, op, c, workBuffers, getType() == XModule::ModuleType_IT);
2060
2061 // Having an effect? Overwrite what we already have...
2062 if (srcPattern[srcIndex+3] || srcPattern[srcIndex+4])
2063 {
2064 eff = srcPattern[srcIndex+3];
2065 op = srcPattern[srcIndex+4];
2066 }
2067
2068 if (eff > 0x0f)
2069 {
2070 eff = op = 0;
2071 }
2072
2073 /*if (srcPattern[srcIndex+2] >= 0x10 && srcPattern[srcIndex+2] <= 0x50 &&
2074 eff == 0 && op == 0)
2075 {
2076 eff = 0x0C;
2077 op = srcPattern[srcIndex+2] - 0x10;
2078 }*/
2079
2080 dstPattern[dstIndex] = (ins & 0xF0) + ((period>>8)&0x0F);
2081 dstPattern[dstIndex+1] = (mp_ubyte)(period&0xFF);
2082 dstPattern[dstIndex+2] = ((ins & 0x0F) << 4) + (eff);
2083 dstPattern[dstIndex+3] = op;
2084
2085 }
2086
2087 }
2088
2089 f.write(dstPattern, 4, numChannels*64);
2090
2091 delete[] srcPattern;
2092 delete[] dstPattern;
2093 }
2094
2095 for (i = 0; i < header.smpnum; i++)
2096 {
2097 mp_uint32 smplen = prep(smp[i].samplen) << 1;
2098 mp_uint32 j = 0;
2099
2100 // Ensure first 2 bytes are zero in non-looping
2101 // samples (for Protracker/Amiga compatibility)
2102 if(!(smp[i].type & 0xef) && smplen >= 2)
2103 {
2104 f.writeWord(0);
2105 j = 2;;
2106 }
2107
2108 if (smp[i].type & 16)
2109 {
2110 for (; j < smplen; j++)
2111 f.writeByte(smp[i].getSampleValue(j) >> 8);
2112 }
2113 else
2114 {
2115 for (; j < smplen; j++)
2116 f.writeByte(smp[i].getSampleValue(j));
2117 }
2118 }
2119
2120 return MP_OK;
2121 }
2122
2123 #ifdef MILKYTRACKER
saveExtendedPattern(const SYSCHAR * fileName) const2124 bool TXMPattern::saveExtendedPattern(const SYSCHAR* fileName) const
2125 {
2126 // Note: For FT2 compatibility, .XP files are fixed at 32 channels.
2127 // TODO: Create a version 2 format for variable channel counts
2128 TWorkBuffers workBuffers;
2129
2130 // ------ start ---------------------------------
2131 XMFile f(fileName, true);
2132
2133 if (!f.isOpenForWriting())
2134 return false;
2135
2136 f.writeWord(0x1);
2137 f.writeWord(rows);
2138
2139 mp_sint32 len = rows * 32 * 5;
2140
2141 mp_ubyte* srcPattern = new mp_ubyte[len];
2142
2143 memset(srcPattern, 0, len);
2144
2145 convertPattern(NULL, this, srcPattern, 32, workBuffers, false);
2146
2147 f.write(srcPattern, 1, len);
2148
2149 delete[] srcPattern;
2150
2151 return true;
2152 }
2153
saveExtendedTrack(const SYSCHAR * fileName,mp_uint32 channel) const2154 bool TXMPattern::saveExtendedTrack(const SYSCHAR* fileName, mp_uint32 channel) const
2155 {
2156 if (channel >= channum)
2157 return false;
2158
2159 TWorkBuffers workBuffers;
2160
2161 // ------ start ---------------------------------
2162 XMFile f(fileName, true);
2163
2164 if (!f.isOpenForWriting())
2165 return false;
2166
2167 f.writeWord(0x1);
2168 f.writeWord(rows);
2169
2170 mp_sint32 len = rows * 5;
2171
2172 mp_ubyte* srcPattern = new mp_ubyte[rows * channum * 5];
2173 mp_ubyte* dstPattern = new mp_ubyte[len];
2174
2175 convertPattern(NULL, this, srcPattern, channum, workBuffers, false);
2176
2177 for (mp_sint32 r = 0; r < rows; r++)
2178 {
2179 mp_sint32 srcIndex = r*this->channum*5 + channel*5;
2180 dstPattern[r*5] = srcPattern[srcIndex];
2181 dstPattern[r*5+1] = srcPattern[srcIndex+1];
2182 dstPattern[r*5+2] = srcPattern[srcIndex+2];
2183 dstPattern[r*5+3] = srcPattern[srcIndex+3];
2184 dstPattern[r*5+4] = srcPattern[srcIndex+4];
2185 }
2186
2187 f.write(dstPattern, 1, len);
2188
2189 delete[] srcPattern;
2190 delete[] dstPattern;
2191
2192 return true;
2193 }
2194
loadExtendedPattern(const SYSCHAR * fileName)2195 bool TXMPattern::loadExtendedPattern(const SYSCHAR* fileName)
2196 {
2197 XMFile f(fileName);
2198
2199 if (f.readWord() != 0x01)
2200 return false;
2201
2202 mp_uword rows = f.readWord();
2203
2204 if (rows == 0 || rows > 256)
2205 return false;
2206
2207 mp_sint32 len = rows * 32 * 5;
2208
2209 mp_ubyte* srcPattern = new mp_ubyte[len];
2210
2211 f.read(srcPattern, 1, len);
2212
2213 // throw away old pattern
2214 delete[] patternData;
2215 this->rows = rows;
2216 this->channum = 32;
2217 this->effnum = 2;
2218
2219 patternData = new mp_ubyte[rows*channum*(effnum*2+2)];
2220
2221 mp_ubyte* slot = srcPattern;
2222
2223 mp_sint32 bc = 0;
2224 for (mp_sint32 r=0;r<rows;r++) {
2225 for (mp_sint32 c=0;c<channum;c++) {
2226
2227 char gl=0;
2228 for (mp_sint32 i=0;i<XModule::numValidXMEffects;i++)
2229 if (slot[3]==XModule::validXMEffects[i]) gl=1;
2230
2231 if (!gl) slot[3]=slot[4]=0;
2232
2233 if ((slot[3]==0xC)||(slot[3]==0x10)) {
2234 slot[4] = XModule::vol64to255(slot[4]);
2235 }
2236
2237 if ((!slot[3])&&(slot[4])) slot[3]=0x20;
2238
2239 if (slot[3]==0xE) {
2240 slot[3]=(slot[4]>>4)+0x30;
2241 slot[4]=slot[4]&0xf;
2242 }
2243
2244 if (slot[3]==0x21) {
2245 slot[3]=(slot[4]>>4)+0x40;
2246 slot[4]=slot[4]&0xf;
2247 }
2248
2249 if (slot[0]==97) slot[0]=XModule::NOTE_OFF;
2250
2251 patternData[bc]=slot[0];
2252 patternData[bc+1]=slot[1];
2253
2254 XModule::convertXMVolumeEffects(slot[2], patternData[bc+2], patternData[bc+3]);
2255
2256 patternData[bc+4]=slot[3];
2257 patternData[bc+5]=slot[4];
2258
2259 bc+=6;
2260 slot+=5;
2261 } // for c
2262
2263 } // for r
2264
2265
2266 delete[] srcPattern;
2267
2268 return true;
2269 }
2270
loadExtendedTrack(const SYSCHAR * fileName,mp_uint32 channel)2271 bool TXMPattern::loadExtendedTrack(const SYSCHAR* fileName, mp_uint32 channel)
2272 {
2273 XMFile f(fileName);
2274
2275 if (f.readWord() != 0x01)
2276 return false;
2277
2278 mp_uword rows = f.readWord();
2279
2280 if (rows == 0 || rows > 256)
2281 return false;
2282
2283 if (rows > this->rows)
2284 rows = this->rows;
2285
2286 mp_sint32 len = rows * 32 * 5;
2287
2288 mp_ubyte* srcPattern = new mp_ubyte[len];
2289
2290 f.read(srcPattern, 1, len);
2291
2292 // throw away old pattern
2293 mp_ubyte* slot = srcPattern;
2294
2295 mp_sint32 bc = 0, r;
2296 for (r=0;r<rows;r++)
2297 {
2298 bc = r*this->channum*(this->effnum*2+2) + channel*(this->effnum*2+2);
2299
2300 char gl=0;
2301 for (mp_sint32 i=0;i<XModule::numValidXMEffects;i++)
2302 if (slot[3]==XModule::validXMEffects[i]) gl=1;
2303
2304 if (!gl) slot[3]=slot[4]=0;
2305
2306 if ((slot[3]==0xC)||(slot[3]==0x10)) {
2307 slot[4] = XModule::vol64to255(slot[4]);
2308 }
2309
2310 if ((!slot[3])&&(slot[4])) slot[3]=0x20;
2311
2312 if (slot[3]==0xE) {
2313 slot[3]=(slot[4]>>4)+0x30;
2314 slot[4]=slot[4]&0xf;
2315 }
2316
2317 if (slot[3]==0x21) {
2318 slot[3]=(slot[4]>>4)+0x40;
2319 slot[4]=slot[4]&0xf;
2320 }
2321
2322 if (slot[0]==97) slot[0]=XModule::NOTE_OFF;
2323
2324 patternData[bc]=slot[0];
2325 patternData[bc+1]=slot[1];
2326
2327 XModule::convertXMVolumeEffects(slot[2], patternData[bc+2], patternData[bc+3]);
2328
2329 patternData[bc+4]=slot[3];
2330 patternData[bc+5]=slot[4];
2331
2332 slot+=5;
2333 } // for r
2334
2335 for (r = rows; r < this->rows; r++)
2336 {
2337 bc = r*this->channum*(this->effnum*2+2) + channel*(this->effnum*2+2);
2338 memset(patternData + bc, 0, (this->effnum*2+2));
2339 }
2340
2341 delete[] srcPattern;
2342
2343 return true;
2344 }
2345
2346 #endif
2347
getNumUsedPatterns()2348 mp_sint32 XModule::getNumUsedPatterns()
2349 {
2350 mp_sint32 i;
2351
2352 mp_sint32 patNum = header.patnum;
2353 for (i = header.patnum - 1; i > 0; i--)
2354 {
2355 TXMPattern* pattern = &phead[i];
2356
2357 if (pattern->patternData == NULL)
2358 continue;
2359
2360 mp_sint32 slotSize = pattern->effnum * 2 + 2;
2361
2362 mp_sint32 patternSize = slotSize * pattern->channum * pattern->rows;
2363
2364 bool empty = true;
2365 for (mp_sint32 j = 0; j < patternSize; j++)
2366 if (pattern->patternData[j])
2367 {
2368 empty = false;
2369 patNum = i+1;
2370 break;
2371 }
2372
2373 if (empty)
2374 {
2375 bool found = false;
2376 for (mp_sint32 j = 0; j < header.ordnum; j++)
2377 if (header.ord[j] == i)
2378 {
2379 found = true;
2380 break;
2381 }
2382
2383 if (found)
2384 {
2385 patNum = i+1;
2386 break;
2387 }
2388 }
2389 else
2390 {
2391 patNum = i+1;
2392 break;
2393 }
2394 }
2395
2396 if (i == 0)
2397 return 0;
2398
2399 return patNum;
2400 }
2401
getNumUsedInstruments()2402 mp_sint32 XModule::getNumUsedInstruments()
2403 {
2404 mp_sint32 i;
2405
2406 mp_sint32 insNum = header.insnum;
2407 for (i = header.insnum - 1; i > 0; i--)
2408 {
2409 mp_ubyte buffer[MP_MAXTEXT+1];
2410
2411 convertStr(reinterpret_cast<char*>(buffer), reinterpret_cast<char*>(instr[i].name), MP_MAXTEXT, false);
2412
2413 if (strlen((char*)buffer))
2414 {
2415 insNum = i+1;
2416 break;
2417 }
2418
2419 if (instr[i].samp)
2420 {
2421
2422 for (mp_sint32 j = 0; j < 120; j++)
2423 {
2424 mp_sint32 s = instr[i].snum[j];
2425 if (s >= 0)
2426 {
2427 convertStr(reinterpret_cast<char*>(buffer), reinterpret_cast<char*>(smp[s].name), MP_MAXTEXT, false);
2428 if (strlen((char*)buffer) || (smp[s].sample && smp[s].samplen))
2429 {
2430 insNum = i+1;
2431 goto insFound;
2432 }
2433 }
2434 }
2435 }
2436 }
2437
2438 insFound:
2439 if (i == 0)
2440 return 0;
2441
2442 return insNum;
2443 }
2444