Initial commit.
[amiga/xmodule.git] / TrackerHook.c
1 /*
2 **      TrackerHook.c
3 **
4 **      Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
5 **
6 **
7 **      Load a Sound/Noise/ProTracker module with 15 or 31 instruments, decode
8 **      it and store into internal structures.
9 **
10 **      Save internal structures to a SoundTracker module with 15 instruments
11 **      or to a 31 instruments Noise/ProTracker unpacked module.
12 */
13
14 #include <exec/memory.h>
15 #include <dos/stdio.h>
16
17 #include <proto/exec.h>
18 #include <proto/dos.h>
19 #include <proto/intuition.h>
20 #include <proto/iffparse.h>
21 #include <proto/xmodule.h>
22
23 #include "XModulePriv.h"
24 #include "Gui.h"
25
26
27
28 /* This structure holds a tracker row */
29
30 struct StRow
31 {
32         UWORD Note;
33         UBYTE InstEff;
34         UBYTE EffVal;
35 };
36
37
38
39 /*      Definitions for various xxxTracker IDs. */
40
41 #define ID_SOUNDTRACKER         'ST15'          /* "ST15" */
42 #define ID_TAKETRACKER          '32CH'          /* "32CH" */
43 #define ID_FASTTRACKER1         '8CHN'          /* "8CHN" */
44 #define ID_NOISETRACKER         0x4D264B21      /* "M&K!" */
45 #define ID_PROTRACKER           0x4D2E4B2E      /* "M.K." */
46 #define ID_PROTRACKER100        0x4D214B21      /* "M!K!" */
47 #define ID_STARTREKKER4         0x464C5434      /* "FLT4" */
48 #define ID_STARTREKKER8         0x464C5438      /* "FLT8" */
49 #define ID_UNICTRACKER          0x454D5733      /* "EMW3" */
50 #define ID_POWERMUSIC           0x21504D21      /* "!PM!" */
51
52
53
54 /* Local function prototypes */
55 static HOOKCALL struct XMHook *IdentifyTracker (
56         REG(d0, BPTR fh),
57         REG(a0, struct XMHook *loader),
58         REG(a1, ULONG *tags));
59 static HOOKCALL struct XMHook *IdentifySoundTracker (
60         REG(d0, BPTR fh),
61         REG(a0, struct XMHook *loader),
62         REG(a1, ULONG *tags));
63 static HOOKCALL LONG SaveTracker (
64         REG(d0, BPTR fh),
65         REG(a0, struct SongInfo *si),
66         REG(a1, struct XMHook *saver),
67         REG(a2, ULONG *tags));
68 static HOOKCALL LONG LoadTracker (
69         REG(d0, BPTR fh),
70         REG(a0, struct SongInfo *si),
71         REG(a1, struct XMHook *loader),
72         REG(a2, ULONG *tags));
73
74 static void SetGlobalSpeed (struct SongInfo *si, UWORD tracks);
75 INLINE UWORD DecodeNote (UWORD Note, UWORD Patt, UWORD Line, UWORD Track);
76 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval);
77
78
79 static const ULONG TakeTrackerIDs[32] =
80 {
81         'TDZ1', 'TDZ2', 'TDZ3', 'M.K.', '5CHN', '6CHN', '7CHN', '8CHN',
82         '9CHN', '10CH', '11CH', '12CH', '13CH', '14CH', '15CH', '16CH',
83         '17CH', '18CH', '19CH', '20CH', '21CH', '22CH', '23CH', '24CH',
84         '25CH', '26CH', '27CH', '28CH', '29CH', '30CH', '31CH', '32CH'
85 };
86
87
88 /* Note conversion table for Sound/Noise/ProTracker */
89 static const UWORD TrackerNotes[] =
90 {
91         0x000,                                                                          /* Null note */
92
93         0x6B0, 0x650, 0x5F5, 0x5A0, 0x54D, 0x501,       /* Octave 0 */
94         0x4B9, 0x475, 0x435, 0x3F9, 0x3C1, 0x38B,
95
96         0x358, 0x328, 0x2FA, 0x2D0, 0x2A6, 0x280,       /* Octave 1 */
97         0x25C, 0x23A, 0x21A, 0x1FC, 0x1E0, 0x1C5,
98
99         0x1AC, 0x194, 0x17D, 0x168, 0x153, 0x140,       /* Octave 2 */
100         0x12E, 0x11d, 0x10D, 0x0FE, 0x0F0, 0x0E2,
101
102         0x0D6, 0x0CA, 0x0BE, 0x0B4, 0x0AA, 0x0A0,       /* Octave 3 */
103         0x097, 0x08F, 0x087, 0x07F, 0x078, 0x071,
104
105         0x06B, 0x065, 0x05F, 0x05A, 0x055, 0x050,       /* Octave 4 */
106         0x04C, 0x047, 0x043, 0x040, 0x03C, 0x039,
107
108         0x035, 0x032, 0x030, 0x02D, 0x02A, 0x028,       /* Octave 5 */
109         0x026, 0x024, 0x022, 0x020, 0x01E, 0x01C
110 };
111
112
113
114 static const UBYTE Effects[MAXTABLEEFFECTS] =
115 {
116 /*    STRK      XModule                 Val */
117
118         0x00,   /*      Null effect             $00     */
119
120         0x01,   /*      Portamento Up   $01     */
121         0x02,   /*      Portamento Down $02     */
122         0x03,   /*      Tone Portamento $03     */
123         0x04,   /*      Vibrato                 $04     */
124         0x05,   /*      ToneP + VolSl   $05     */
125         0x06,   /*      Vibra + VolSl   $06     */
126         0x07,   /*      Tremolo                 $07     */
127         0x08,   /*      Set Hold/Decay  $08     */
128         0x09,   /*      Sample Offset   $09     */
129         0x0A,   /*      Volume Slide    $0A     */
130         0x0B,   /*      Position Jump   $0B     */
131         0x0C,   /*      Set Volume              $0C     */
132         0x0D,   /*      Pattern break   $0D     */
133         0x0E,   /*      Misc                    $0E     */
134         0x0F,   /*      Set Speed               $0F     */
135         0x0F,   /*      Set Tempo               $10     */
136         0x00,   /*      Arpeggio                $11     */
137
138         0x03,   /*      Oktalyzer H                     */
139         0x03,   /*      Oktalyzer L                     */
140 };
141
142
143
144 static struct XMHook *ProTrackerLoader = NULL;
145
146
147
148 static HOOKCALL struct XMHook *IdentifyTracker (
149         REG(d0, BPTR fh),
150         REG(a0, struct XMHook *loader),
151         REG(a1, ULONG *tags))
152
153 /* Determine if the given file is an XyzTracker module.
154  * Note: the file position will be changed on exit.
155  */
156 {
157         LONG id;
158         ULONG i;
159
160         Seek (fh, 1080, OFFSET_BEGINNING);
161         if (FRead (fh, &id, 4, 1) != 1)
162                 return NULL;
163
164         /* Check Noise/ProTracker */
165         if ((id == ID_PROTRACKER) || (id == ID_PROTRACKER100) || (id == ID_NOISETRACKER) ||
166                 (id == ID_UNICTRACKER) || (id == ID_STARTREKKER4))
167                 return loader;
168
169         /* Check for Multi/Fast Tracker */
170         for (i = 0; i < 32; i++)
171                 if (id == TakeTrackerIDs[i])
172                         return loader;
173
174         return NULL;
175 }
176
177
178
179 static HOOKCALL struct XMHook *IdentifySoundTracker (
180         REG(d0, BPTR fh),
181         REG(a0, struct XMHook *loader),
182         REG(a1, ULONG *tags))
183
184 /* Last chance loader: SoundTracker15 or weird ProTracker module */
185 {
186         switch (ShowRequestArgs (MSG_UNKNOWN_MOD_FORMAT, MSG_SOUND_PRO_CANCEL, NULL))
187         {
188                 case 1:
189                         return loader;
190
191                 case 2:
192                         return ProTrackerLoader;
193
194                 default:
195                         return NULL;
196         }
197 }
198
199
200
201 static HOOKCALL LONG SaveTracker (
202         REG(d0, BPTR fh),
203         REG(a0, struct SongInfo *si),
204         REG(a1, struct XMHook *saver),
205         REG(a2, ULONG *tags))
206 {
207         struct Instrument *instr;
208         ULONG   pattcount = 0, maxnumpatt = 64, numtracks = 4, songlength;
209         ULONG   l;
210         ULONG   i, j, k;        /* Loop counters */
211         UBYTE c;
212         struct { UWORD Note; UBYTE InstEff; UBYTE EffVal; }
213                 strow; /* Temporary storage for a SoundTracker row */
214
215
216
217         /* Calculate pattcount */
218
219         songlength = min (si->Length, 128);
220
221         for (i = 0 ; i < songlength ; i++)
222                 if (si->Sequence[i] > pattcount)
223                         pattcount = si->Sequence[i];
224
225         pattcount++;    /* Pattern numbering starts from 0 */
226
227
228         /* Write Song name */
229         {
230                 UBYTE name[20] = {0};
231
232                 /* Tracker modules do not require the NULL terminator */
233                 if (si->Title) strncpy (name, si->Title, 20);
234
235                 if (FWrite (fh, name, 20, 1) != 1) return ERROR_IOERR;
236         }
237
238
239         /* Write instruments name, length, volume, repeat, replen */
240
241         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
242                 (APTR)MSG_WRITING_INSTINFO, NULL);
243
244
245         for ( j = 1 ; j < (((ULONG)saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32) ; j++ )
246         {
247                 struct
248                 {
249                         UBYTE namebuf[22];
250                         UWORD length;
251                         UWORD volume;
252                         UWORD repeat;
253                         UWORD replen;
254                 } modinstr;
255
256
257                 memset (&modinstr, 0, sizeof (modinstr));
258
259                 if (instr = si->Instr[j])
260                 {
261                         /* Some Trackers require the instrument buffer to be
262                          * padded with 0's.
263                          */
264                         strncpy (modinstr.namebuf, instr->Name, 22);
265
266                         /* SoundTracker cannot handle instruments longer than 64K */
267                         if (instr->Length > 0xFFFE)
268                         {
269                                 modinstr.length = 0x7FFF;
270                                 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_WARNING,
271                                         (APTR)MSG_INSTR_TOO_LONG, j);
272                         }
273                         else modinstr.length = instr->Length >> 1;
274
275                         if (!instr->Sample) modinstr.length = 0;
276
277
278                         if (saver->xmh_ID == ID_SOUNDTRACKER)
279                                 modinstr.volume = instr->Volume;
280                         else
281                                 modinstr.volume = instr->Volume | ((instr->FineTune & 0x0F) << 8);
282
283                         modinstr.repeat = instr->Repeat >> 1;
284                         modinstr.replen = instr->Replen >> 1;
285                         if (!modinstr.replen) modinstr.replen = 1;
286                 }
287
288                 if (FWrite (fh, &modinstr, sizeof (modinstr), 1) != 1)
289                         return ERROR_IOERR;
290         }
291
292
293         /******************************************/
294         /* Put number of positions in song (BYTE) */
295         /******************************************/
296
297         if (FPutC (fh, songlength) == ENDSTREAMCH)
298                 return ERROR_IOERR;
299
300
301         /*****************************************/
302         /* Put number of patterns in song (BYTE) */
303         /*****************************************/
304
305         switch (saver->xmh_ID)
306         {
307                 case ID_SOUNDTRACKER:
308                         /* SoundTracker stores the number of patterns here */
309                         c = pattcount;
310                         break;
311
312                 case ID_STARTREKKER4:
313                         /* StarTrekker modules store restart value here */
314                         c = si->RestartPos;
315                         break;
316
317                 default:
318                         /* Noise/ProTracker stores $7F as the number of patterns.
319                          * The correct number of patterns is calculated by looking
320                          * for the highest pattern referenced in the position table.
321                          * Therefore, unused patterns MUST be removed, or the Tracker
322                          * will get confused loading the module.
323                          */
324                         c = 0x7F;
325                         break;
326         }
327
328         if (FPutC (fh, c) == ENDSTREAMCH)
329                 return ERROR_IOERR;
330
331
332         /********************/
333         /* Choose module ID */
334         /********************/
335
336         l = saver->xmh_ID;
337
338         switch (l)
339         {
340                 case ID_PROTRACKER:
341                         if (pattcount > 64)
342                         {
343                                 l = ID_PROTRACKER100;
344                                 maxnumpatt = 100;
345                                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
346                                         (APTR)MSG_EXCEEDS_64_PATTS, NULL);
347                         }
348                         break;
349
350                 case ID_PROTRACKER100:
351                         maxnumpatt = 100;
352                         if (pattcount <= 64)
353                                 l = ID_PROTRACKER;
354                         break;
355
356                 case ID_TAKETRACKER:
357                         numtracks = si->MaxTracks;
358                         l = TakeTrackerIDs[si->MaxTracks - 1];
359                         maxnumpatt = 100;
360                         break;
361
362                 default:
363                         break;
364         }
365
366         if (pattcount >= maxnumpatt)
367         {
368                 pattcount = maxnumpatt;
369                 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
370                         (APTR)MSG_EXCEEDS_MAXPAATTS, maxnumpatt);
371         }
372
373
374         /************************/
375         /* Write position table */
376         /************************/
377         {
378                 UBYTE postable[128];
379
380                 memset (postable, 0, 128);
381
382                 /* All this is needed because ProTracker has serious
383                  * problems dealing with modules whose postable has
384                  * references to inexistant patterns.
385                  */
386                 for (i = 0; i < songlength; i++)
387                 {
388                         postable[i] = si->Sequence[i];
389
390                         if (postable[i] >= pattcount)
391                                 postable[i] = 0;
392                 }
393
394                 if (FWrite (fh, postable, 1, 128) != 128) return ERROR_IOERR;
395         }
396
397
398         /*******************/
399         /* Write module ID */
400         /*******************/
401
402         if (l != ID_SOUNDTRACKER)
403                 if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
404
405
406
407         /**********************/
408         /* Write pattern data */
409         /**********************/
410
411         SetGlobalSpeed (si, numtracks);
412
413         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
414                 (APTR)MSG_WRITING_PATTS, NULL);
415
416         for (i = 0 ; i < pattcount ; i++)
417         {
418                 struct Note **tracks = si->Patt[i]->Notes;
419
420                 if (xmDisplayProgress (i, pattcount))
421                         return ERROR_BREAK;
422
423                 for (j = 0 ; j < 0x40 ; j++)
424                 {
425                         for (k = 0 ; k < numtracks ; k++)
426                         {
427                                 struct Note *note = &tracks[k][j];
428
429                                 /* Translate Note */
430                                 strow.Note = TrackerNotes[note->Note];
431
432                                 /* Translate instr # (high nibble) & effect (low nibble) */
433                                 c = note->Inst;
434
435                                 if ((c > 0x0F) && (saver->xmh_ID != ID_SOUNDTRACKER))
436                                 {
437                                         /* Noise/ProTracker stores the high bit of the
438                                          * instrument number in bit 15 of the note value.
439                                          */
440                                         strow.Note |= 0x1000;
441                                 }
442
443                                 strow.InstEff = (c << 4) | Effects[note->EffNum];
444
445                                 /* Copy effect value */
446                                 strow.EffVal = note->EffVal;
447
448                                 /* Write the Row */
449                                 if (FWrite (fh, &strow, 4, 1) != 1) return ERROR_IOERR;
450                         }
451                 }
452         }
453
454         /* Instruments are written using non buffered I/O because it's
455          * generally more efficient when writing big buffers.
456          */
457         Flush (fh);
458
459         /********************/
460         /* Save Instruments */
461         /********************/
462
463         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
464                 (APTR)MSG_WRITING_INSTDATA, NULL);
465
466         for (i = 1 ; i < (saver->xmh_ID == ID_SOUNDTRACKER ? 16 : 32) ; i++)
467         {
468                 ULONG len;
469
470                 if (!(instr = si->Instr[i])) continue;
471
472                 len = instr->Length & (~1);
473
474                 /* Adapt instrument to SoundTracker 64K limit */
475                 if (len > 0xFFFE) len = 0xFFFE;
476
477                 if (xmDisplayProgress (i, (saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32))
478                         return ERROR_BREAK;
479
480                 if (len)
481                         if (Write (fh, instr->Sample, len) != len)
482                                 return ERROR_IOERR;
483         }
484
485         return (0);
486 }
487
488
489
490 static HOOKCALL LONG LoadTracker (
491         REG(d0, BPTR fh),
492         REG(a0, struct SongInfo *si),
493         REG(a1, struct XMHook *loader),
494         REG(a2, ULONG *tags))
495 {
496         struct Note *note;
497         struct Instrument       *instr;
498         struct Pattern          *patt;
499         struct StRow            *stpatt,        /* Temporary storage for a Tracker pattern */
500                                                 *strow;
501
502         ULONG i, j, k;          /* Loop counters */
503         ULONG pattsize;
504         UWORD numtracks = 4, numpatterns, songlen;
505         UBYTE c;                        /* Read buffers */
506
507         ULONG soundtracker = (loader->xmh_ID == ID_SOUNDTRACKER);
508
509
510         /* Get the score name */
511         {
512                 UBYTE name[21];
513
514                 if (FRead (fh, name, 20, 1) != 1) return ERROR_IOERR;
515                 name[20] = '\0'; /* Ensure the string is Null-terminated */
516
517                 SetAttrs (si,
518                         SNGA_Title, name,
519                         TAG_DONE);
520         }
521
522         /* Get the Instruments name, length, cycle, effects */
523
524         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
525                 (APTR)MSG_READING_INSTS_INFO, NULL);
526
527         for ( j = 1 ; j < (soundtracker ? 16 : 32); j++ )
528         {
529                 struct
530                 {
531                         UBYTE   namebuf[22];
532                         UWORD   length;
533                         BYTE    finetune;
534                         UBYTE   volume;
535                         UWORD   repeat;
536                         UWORD   replen;
537                 } modinstr;
538
539
540                 memset (&modinstr, 0, sizeof (modinstr));
541
542                 if (FRead (fh, &modinstr, sizeof (modinstr), 1) != 1)
543                         return ERROR_IOERR;
544
545                 if (modinstr.namebuf[0] || modinstr.length)
546                 {
547                         modinstr.namebuf[21] = '\0'; /* Ensure the string is Null-terminated */
548
549                         if (!xmAddInstrument (si, j,
550                                 INSTRA_Name,            modinstr.namebuf,
551                                 INSTRA_Length,          modinstr.length << 1,
552                                 INSTRA_FineTune,        (modinstr.finetune & 0x08) ?    /* Fix sign */
553                                         (modinstr.finetune | 0xF0) : modinstr.finetune,
554                                 INSTRA_Volume,          modinstr.volume,
555                                 INSTRA_Repeat,          modinstr.repeat << 1,
556                                 INSTRA_Replen,          (modinstr.replen == 1) ? 0 : (modinstr.replen << 1),
557                                 TAG_DONE))
558                                 return ERROR_NO_FREE_STORE;
559                 }
560         }
561
562
563         /* Read song length */
564
565         if ((songlen = FGetC (fh)) == ENDSTREAMCH) return ERROR_IOERR;
566
567         /* Read & Ignore number of song patterns.
568          *
569          * Noise/ProTracker stores $7F as the number of patterns.
570          * The correct number of patterns is calculated by looking
571          * for the highest pattern referenced in the position table.
572          * The OctaMED saver wrongly puts the number of patterns
573          * when saving as a ProTracker module.
574          * SoundTracker should save the correct number of patterns,
575          * but some 15 instrument modules I found had incorrect
576          * values here.  So we always ignore this field.
577          */
578         if (FGetC (fh) == ENDSTREAMCH) return ERROR_IOERR;
579
580
581         /* Read the song sequence */
582         {
583                 UBYTE postable[128];
584
585                 if (FRead (fh, postable, 1, 128) != 128) return ERROR_IOERR;
586
587                 if (!(xmSetSongLen (si, songlen)))
588                         return ERROR_NO_FREE_STORE;
589
590                 for (i = 0; i < si->Length; i++)
591                         si->Sequence[i] = postable[i];
592
593                 /* Search for the highest pattern referenced in the position
594                  * table to find out the actual number of patterns.
595                  *
596                  * Important note: Some modules do have position table
597                  * entries beyond the song length.  These references *must*
598                  * be taken into account to calculate the last pattern, so
599                  * looping only <songlen> times here is *wrong*.  The
600                  * correct behaviour is to always look all of the 128
601                  * position table entries.
602                  */
603
604                 numpatterns = 0;
605
606                 for (i = 0 ; i < 128 ; i++)
607                         if (postable[i] > numpatterns)
608                                 numpatterns = postable[i];
609
610                 numpatterns++;  /* Pattern numbering starts from 0 */
611         }
612
613
614
615         /* Check module ID */
616
617         if (!soundtracker)
618         {
619                 ULONG __aligned id = 0;
620
621                 /* The module ID could have been stripped away in modules
622                  * coming from games and intros to make the module harder
623                  * to rip, therefore its absence is acceptable.
624                  */
625                 if (FRead (fh, &id, 4, 1) != 1) return ERROR_IOERR;
626
627
628                 /* Check for FastTracker/TakeTracker IDs */
629
630                 for (i = 0; i < 32; i++)
631                 {
632                         if (id == TakeTrackerIDs[i])
633                         {
634                                 numtracks = i + 1;
635                                 break;
636                         }
637                 }
638
639                 if (id)
640                 {
641                         UBYTE buf[5];
642
643                         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
644                                 (APTR)MSG_MODULE_ID, IDtoStr (id, buf));
645                 }
646         }
647
648
649         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
650                 (APTR)MSG_MODULE_HAS_N_CHN, numtracks);
651
652
653         /* Allocate memory for a full SoundTracker pattern */
654
655         si->MaxTracks = numtracks;
656         pattsize = (sizeof (struct StRow) * 0x40) * numtracks;
657
658         if (!(stpatt = AllocMem (pattsize, 0)))
659                 return ERROR_NO_FREE_STORE;
660
661
662         /* Read pattern data */
663
664         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
665                 (APTR)MSG_READING_PATTS, NULL);
666
667         for (i = 0; i < numpatterns; i++)
668         {
669                 if (xmDisplayProgress (i, numpatterns))
670                 {
671                         FreeMem (stpatt, pattsize);
672                         return ERROR_BREAK;
673                 }
674
675                 /* Read a whole Tracker row */
676                 if (FRead (fh, stpatt, pattsize, 1) != 1)
677                 {
678                         FreeMem (stpatt, pattsize);
679                         return ERROR_IOERR;
680                 }
681
682                 /* Reset note counter */
683                 strow = stpatt;
684
685                 /* Allocate memory for pattern */
686                 if (!(patt = xmAddPattern (si,
687                         PATTA_Tracks,   numtracks,
688                         PATTA_Lines,    64,
689                         TAG_DONE)))
690                 {
691                         FreeMem (stpatt, pattsize);
692                         return ERROR_NO_FREE_STORE;
693                 }
694
695                 for ( j = 0 ; j < 0x40 ; j++ )
696                 {
697                         for ( k = 0 ; k < numtracks ; k++, strow++ )
698                         {
699                                 /* Get address of the current pattern row */
700                                 note = &patt->Notes[k][j];
701
702                                 /* Decode note (highest nibble is cleared) */
703                                 note->Note = DecodeNote (strow->Note & 0xFFF, i, j, k);
704
705                                 /* Decode instrument number (high nibble) */
706                                 c = strow->InstEff >> 4; /* Get instrument nr. */
707                                 if (!soundtracker && (strow->Note & 0x1000))
708                                         c |= 0x10;      /* High bit of Noise/ProTracker instrument */
709                                 note->Inst = c;
710
711                                 /* Decode effect (low nibble) */
712                                 note->EffNum = DecodeEff (strow->InstEff & 0x0F, strow->EffVal);
713
714                                 /* Copy effect value */
715                                 note->EffVal = strow->EffVal;
716                         }
717                 }
718         }
719
720         FreeMem (stpatt, pattsize);
721
722
723         /* Look for a SetSpeed command ($F) in the first row and
724          * set si->GlobalSpeed.  If Speed is higher than 31,
725          * si->GlobalTempo should be initialized instead. (TODO)
726          */
727
728         SetAttrs (si,
729                 SNGA_Author, -1,
730                 SNGA_GlobalSpeed,       6,
731                 SNGA_GlobalTempo,       125,
732                 TAG_DONE);
733
734
735         /* Load Instruments */
736
737         xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
738                 (APTR)MSG_READING_INSTS, NULL);
739
740
741         for (j = 1 ; j < (soundtracker ? 16 : 32) ; j++)
742         {
743                 BYTE *sample;
744
745                 /* Check if instrument exists */
746
747                 if (!(instr = si->Instr[j])) continue;
748                 if (instr->Length == 0) continue;
749
750                 if (xmDisplayProgress (j, soundtracker ? 16 : 32))
751                         return ERROR_BREAK;
752
753                 if (!(sample = (BYTE *) AllocVec (instr->Length, MEMF_SAMPLE)))
754                         return ERROR_NO_FREE_STORE;
755
756                 if (FRead (fh, sample, instr->Length, 1) != 1)
757                 {
758                         FreeVec (sample);
759
760                         /* Clear instrument lengths */
761                         for (i = j; i < 31; i++)
762                                 if (si->Instr[i]) si->Instr[i]->Length = 0;
763
764                         if (j == 0)
765                         {
766                                 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
767                                         (APTR)MSG_SONG_HAS_NO_INSTS, NULL);
768
769                                 return RETURN_WARN;     /* Tell 'em this is a damn song */
770                         }
771
772                         return ERROR_IOERR;
773                 }
774
775                 xmSetInstrument (si, j,
776                         INSTRA_Sample, sample,
777                         TAG_DONE);
778         }
779
780         /* Check for extra data following the module */
781         if (FGetC (fh) != ENDSTREAMCH)
782                 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_NOTE,
783                         (APTR)MSG_EXTRA_DATA_AFTER_MOD, NULL);
784
785         return RETURN_OK;
786 }
787
788
789
790 INLINE UWORD DecodeNote (UWORD Note, UWORD Patt, UWORD Line, UWORD Track)
791 {
792         if (!Note) return 0;
793
794         {
795                 ULONG n, mid, low = 1, high = MAXNOTES-1;
796
797                 /* The nice binary search learnt at school ;-) */
798                 do
799                 {
800                         mid = (low + high) / 2;
801                         if ((n = TrackerNotes[mid]) > Note) low = mid + 1;
802                         else if (n < Note) high = mid - 1;
803                         else return (UWORD)mid;
804                 } while (low <= high);
805         }
806         xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
807                 (APTR)MSG_INVALID_NOTE, Note, Patt, Track, Line);
808         return 0;
809 }
810
811
812
813 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval)
814 {
815         UBYTE i;
816
817         if (eff == 0 && effval)
818                 return (EFF_ARPEGGIO);
819
820         if (eff == 0x0F) /* Speed */
821         {
822                 if (effval < 0x20)
823                         return EFF_SETSPEED;
824                 else
825                         return EFF_SETTEMPO;
826         }
827
828         for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
829                 if (eff == Effects[i])
830                         return i;
831
832         return 0;
833 }
834
835
836
837 static void SetGlobalSpeed (struct SongInfo *si, UWORD tracks)
838
839 /* Put a speed command ($F) at the first line played in the song */
840 {
841         struct Pattern *patt = si->Patt[si->Sequence[0]];
842         struct Note **pn = patt->Notes;
843         ULONG i;
844
845         tracks = min (tracks, patt->Tracks);
846
847         /* Do it only if required */
848         if (si->GlobalSpeed != DEF_SONGSPEED)
849         {
850                 /* Ensure a SetSpeed command does not exist yet in the first row... */
851                 for (i = 0 ; i < tracks ; i++)
852                         if (pn[i][0].EffNum == EFF_SETSPEED)
853                                 goto settempo;  /* Speed is already set, now for the Tempo... */
854
855                 /* Try to find a free effect slot in the row... */
856                 for (i = 0 ; i < tracks ; i++)
857                         if (pn[i][0].EffNum == EFF_NULL && pn[i][0].EffVal == 0)
858                                         break;
859
860                 pn[i][0].EffNum = EFF_SETSPEED;
861                 pn[i][0].EffVal = si->GlobalSpeed;
862         }
863
864 settempo:
865         if (si->GlobalTempo != DEF_SONGTEMPO)
866         {
867                 /* Ensure a SetTempo command does not exist yet in the first row... */
868                 for (i = 0 ; i < tracks ; i++)
869                         if (pn[i][0].EffNum == EFF_SETTEMPO)
870                                 return; /* Tempo is already set, nothing else to do... */
871
872                 /* Try to find a free effect slot in the row... */
873                 for (i = 0 ; i < tracks ; i++)
874                         if (pn[i][0].EffNum == 0 && pn[i][0].EffVal == 0)
875                                 break;
876
877                 pn[i][0].EffNum = EFF_SETTEMPO;
878                 pn[i][0].EffVal = si->GlobalTempo;
879         }
880 }
881
882
883
884 GLOBALCALL void AddTrackerHooks (void)
885
886 /* Adds Tracker loaders and savers */
887 {
888         ProTrackerLoader = xmAddHook (
889                 XMHOOK_Type,                    NT_XMLOADER,
890                 XMHOOK_Name,                    (LONG)"Tracker",
891                 XMHOOK_Priority,                10,
892                 XMHOOK_Descr,                   (LONG)"Sound/Noise/Star/Pro/Fast/TakeTracker",
893                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
894                 XMHOOK_ID,                              ID_PROTRACKER,
895                 XMHOOK_Flags,                   XMHF_INTERNAL,
896                 XMHOOK_LoadModFunc,             LoadTracker,
897                 XMHOOK_IdentifyModFunc, IdentifyTracker,
898                 XMHOOK_MaxTracks,               32,
899                 XMHOOK_MaxPatterns,             255,
900                 XMHOOK_MaxInstruments,  31,
901                 XMHOOK_MaxLength,               128,
902                 XMHOOK_MaxSampleLen,    65534,
903                 XMHOOK_MaxPattLen,              64,
904                 TAG_DONE);
905
906         xmAddHook (
907                 XMHOOK_Type,                    NT_XMLOADER,
908                 XMHOOK_Name,                    (LONG)"SoundTracker",
909                 XMHOOK_Priority,                -100,
910                 XMHOOK_Descr,                   (LONG)"SoundTracker 15 instruments",
911                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
912                 XMHOOK_ID,                              ID_SOUNDTRACKER,
913                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_FIXED_PATT_LEN,
914                 XMHOOK_LoadModFunc,             LoadTracker,
915                 XMHOOK_IdentifyModFunc, IdentifySoundTracker,
916                 XMHOOK_MaxTracks,               4,
917                 XMHOOK_MaxPatterns,             64,
918                 XMHOOK_MaxInstruments,  15,
919                 XMHOOK_MaxLength,               128,
920                 XMHOOK_MaxSampleLen,    65534,
921                 XMHOOK_MaxPattLen,              64,
922                 TAG_DONE);
923
924         xmAddHook (
925                 XMHOOK_Type,                    NT_XMSAVER,
926                 XMHOOK_Name,                    (LONG)"TakeTracker",
927                 XMHOOK_Priority,                20,
928                 XMHOOK_Descr,                   (LONG)"TakeTracker",
929                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
930                 XMHOOK_ID,                              ID_TAKETRACKER,
931                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
932                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
933                 XMHOOK_SaveModFunc,             SaveTracker,
934                 XMHOOK_MaxTracks,               32,
935                 XMHOOK_MaxPatterns,             100,
936                 XMHOOK_MaxInstruments,  31,
937                 XMHOOK_MaxLength,               128,
938                 XMHOOK_MaxSampleLen,    65534,
939                 XMHOOK_MaxPattLen,              64,
940                 TAG_DONE);
941
942         xmAddHook (
943                 XMHOOK_Type,                    NT_XMSAVER,
944                 XMHOOK_Name,                    (LONG)"FastTracker1",
945                 XMHOOK_Priority,                20,
946                 XMHOOK_Descr,                   (LONG)"FastTracker 1.0",
947                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
948                 XMHOOK_ID,                              ID_FASTTRACKER1,
949                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
950                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
951                 XMHOOK_SaveModFunc,             SaveTracker,
952                 XMHOOK_MaxTracks,               8,
953                 XMHOOK_MaxPatterns,             128,
954                 XMHOOK_MaxInstruments,  31,
955                 XMHOOK_MaxLength,               128,
956                 XMHOOK_MaxSampleLen,    65534,
957                 XMHOOK_MaxPattLen,              64,
958                 TAG_DONE);
959
960         xmAddHook (
961                 XMHOOK_Type,                    NT_XMSAVER,
962                 XMHOOK_Name,                    (LONG)"ProTracker",
963                 XMHOOK_Priority,                20,
964                 XMHOOK_Descr,                   (LONG)"ProTracker 2.x - 3.x",
965                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
966                 XMHOOK_ID,                              ID_PROTRACKER,
967                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
968                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
969                 XMHOOK_SaveModFunc,             SaveTracker,
970                 XMHOOK_MaxTracks,               4,
971                 XMHOOK_MaxPatterns,             64,
972                 XMHOOK_MaxInstruments,  31,
973                 XMHOOK_MaxLength,               128,
974                 XMHOOK_MaxSampleLen,    65534,
975                 XMHOOK_MaxPattLen,              64,
976                 TAG_DONE);
977
978         xmAddHook (
979                 XMHOOK_Type,                    NT_XMSAVER,
980                 XMHOOK_Name,                    (LONG)"ProTracker100",
981                 XMHOOK_Priority,                15,
982                 XMHOOK_Descr,                   (LONG)"ProTracker 2.3 (100 patterns)",
983                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
984                 XMHOOK_ID,                              ID_PROTRACKER100,
985                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
986                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
987                 XMHOOK_SaveModFunc,             SaveTracker,
988                 XMHOOK_MaxTracks,               4,
989                 XMHOOK_MaxPatterns,             100,
990                 XMHOOK_MaxInstruments,  31,
991                 XMHOOK_MaxLength,               128,
992                 XMHOOK_MaxSampleLen,    65534,
993                 XMHOOK_MaxPattLen,              64,
994                 TAG_DONE);
995
996         xmAddHook (
997                 XMHOOK_Type,                    NT_XMSAVER,
998                 XMHOOK_Name,                    (LONG)"NoiseTracker",
999                 XMHOOK_Priority,                15,
1000                 XMHOOK_Descr,                   (LONG)"NoiseTracker 31 instruments",
1001                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
1002                 XMHOOK_ID,                              ID_NOISETRACKER,
1003                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
1004                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
1005                 XMHOOK_SaveModFunc,             SaveTracker,
1006                 XMHOOK_MaxTracks,               4,
1007                 XMHOOK_MaxPatterns,             64,
1008                 XMHOOK_MaxInstruments,  31,
1009                 XMHOOK_MaxLength,               128,
1010                 XMHOOK_MaxSampleLen,    65534,
1011                 XMHOOK_MaxPattLen,              64,
1012                 TAG_DONE);
1013
1014         xmAddHook (
1015                 XMHOOK_Type,                    NT_XMSAVER,
1016                 XMHOOK_Name,                    (LONG)"StarTrekker4",
1017                 XMHOOK_Priority,                10,
1018                 XMHOOK_Descr,                   (LONG)"StarTrakker 4 channels",
1019                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
1020                 XMHOOK_ID,                              ID_STARTREKKER4,
1021                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
1022                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
1023                 XMHOOK_SaveModFunc,             SaveTracker,
1024                 XMHOOK_MaxTracks,               4,
1025                 XMHOOK_MaxPatterns,             64,
1026                 XMHOOK_MaxInstruments,  31,
1027                 XMHOOK_MaxLength,               128,
1028                 XMHOOK_MaxSampleLen,    65534,
1029                 XMHOOK_MaxPattLen,              64,
1030                 TAG_DONE);
1031
1032         xmAddHook (
1033                 XMHOOK_Type,                    NT_XMSAVER,
1034                 XMHOOK_Name,                    (LONG)"SoundTracker",
1035                 XMHOOK_Descr,                   (LONG)"Old SoundTracker 15 instruments",
1036                 XMHOOK_Author,                  (LONG)"Bernardo Innocenti",
1037                 XMHOOK_ID,                              ID_SOUNDTRACKER,
1038                 XMHOOK_Flags,                   XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS |
1039                                                                 XMHF_EXCLUDE_NAMES | XMHF_FIXED_PATT_LEN,
1040                 XMHOOK_SaveModFunc,             SaveTracker,
1041                 XMHOOK_UserData,                ID_SOUNDTRACKER,
1042                 XMHOOK_MaxTracks,               4,
1043                 XMHOOK_MaxPatterns,             64,
1044                 XMHOOK_MaxInstruments,  15,
1045                 XMHOOK_MaxLength,               128,
1046                 XMHOOK_MaxSampleLen,    65534,
1047                 XMHOOK_MaxPattLen,              64,
1048                 TAG_DONE);
1049 }