4 ** Copyright (C) 1993,94,95,96,97 Bernardo Innocenti
7 ** Load a Sound/Noise/ProTracker module with 15 or 31 instruments, decode
8 ** it and store into internal structures.
10 ** Save internal structures to a SoundTracker module with 15 instruments
11 ** or to a 31 instruments Noise/ProTracker unpacked module.
14 #include <exec/memory.h>
15 #include <dos/stdio.h>
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>
23 #include "XModulePriv.h"
28 /* This structure holds a tracker row */
39 /* Definitions for various xxxTracker IDs. */
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!" */
54 /* Local function prototypes */
55 static HOOKCALL struct XMHook *IdentifyTracker (
57 REG(a0, struct XMHook *loader),
58 REG(a1, ULONG *tags));
59 static HOOKCALL struct XMHook *IdentifySoundTracker (
61 REG(a0, struct XMHook *loader),
62 REG(a1, ULONG *tags));
63 static HOOKCALL LONG SaveTracker (
65 REG(a0, struct SongInfo *si),
66 REG(a1, struct XMHook *saver),
67 REG(a2, ULONG *tags));
68 static HOOKCALL LONG LoadTracker (
70 REG(a0, struct SongInfo *si),
71 REG(a1, struct XMHook *loader),
72 REG(a2, ULONG *tags));
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);
79 static const ULONG TakeTrackerIDs[32] =
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'
88 /* Note conversion table for Sound/Noise/ProTracker */
89 static const UWORD TrackerNotes[] =
91 0x000, /* Null note */
93 0x6B0, 0x650, 0x5F5, 0x5A0, 0x54D, 0x501, /* Octave 0 */
94 0x4B9, 0x475, 0x435, 0x3F9, 0x3C1, 0x38B,
96 0x358, 0x328, 0x2FA, 0x2D0, 0x2A6, 0x280, /* Octave 1 */
97 0x25C, 0x23A, 0x21A, 0x1FC, 0x1E0, 0x1C5,
99 0x1AC, 0x194, 0x17D, 0x168, 0x153, 0x140, /* Octave 2 */
100 0x12E, 0x11d, 0x10D, 0x0FE, 0x0F0, 0x0E2,
102 0x0D6, 0x0CA, 0x0BE, 0x0B4, 0x0AA, 0x0A0, /* Octave 3 */
103 0x097, 0x08F, 0x087, 0x07F, 0x078, 0x071,
105 0x06B, 0x065, 0x05F, 0x05A, 0x055, 0x050, /* Octave 4 */
106 0x04C, 0x047, 0x043, 0x040, 0x03C, 0x039,
108 0x035, 0x032, 0x030, 0x02D, 0x02A, 0x028, /* Octave 5 */
109 0x026, 0x024, 0x022, 0x020, 0x01E, 0x01C
114 static const UBYTE Effects[MAXTABLEEFFECTS] =
116 /* STRK XModule Val */
118 0x00, /* Null effect $00 */
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 */
134 0x0F, /* Set Speed $0F */
135 0x0F, /* Set Tempo $10 */
136 0x00, /* Arpeggio $11 */
138 0x03, /* Oktalyzer H */
139 0x03, /* Oktalyzer L */
144 static struct XMHook *ProTrackerLoader = NULL;
148 static HOOKCALL struct XMHook *IdentifyTracker (
150 REG(a0, struct XMHook *loader),
151 REG(a1, ULONG *tags))
153 /* Determine if the given file is an XyzTracker module.
154 * Note: the file position will be changed on exit.
160 Seek (fh, 1080, OFFSET_BEGINNING);
161 if (FRead (fh, &id, 4, 1) != 1)
164 /* Check Noise/ProTracker */
165 if ((id == ID_PROTRACKER) || (id == ID_PROTRACKER100) || (id == ID_NOISETRACKER) ||
166 (id == ID_UNICTRACKER) || (id == ID_STARTREKKER4))
169 /* Check for Multi/Fast Tracker */
170 for (i = 0; i < 32; i++)
171 if (id == TakeTrackerIDs[i])
179 static HOOKCALL struct XMHook *IdentifySoundTracker (
181 REG(a0, struct XMHook *loader),
182 REG(a1, ULONG *tags))
184 /* Last chance loader: SoundTracker15 or weird ProTracker module */
186 switch (ShowRequestArgs (MSG_UNKNOWN_MOD_FORMAT, MSG_SOUND_PRO_CANCEL, NULL))
192 return ProTrackerLoader;
201 static HOOKCALL LONG SaveTracker (
203 REG(a0, struct SongInfo *si),
204 REG(a1, struct XMHook *saver),
205 REG(a2, ULONG *tags))
207 struct Instrument *instr;
208 ULONG pattcount = 0, maxnumpatt = 64, numtracks = 4, songlength;
210 ULONG i, j, k; /* Loop counters */
212 struct { UWORD Note; UBYTE InstEff; UBYTE EffVal; }
213 strow; /* Temporary storage for a SoundTracker row */
217 /* Calculate pattcount */
219 songlength = min (si->Length, 128);
221 for (i = 0 ; i < songlength ; i++)
222 if (si->Sequence[i] > pattcount)
223 pattcount = si->Sequence[i];
225 pattcount++; /* Pattern numbering starts from 0 */
228 /* Write Song name */
230 UBYTE name[20] = {0};
232 /* Tracker modules do not require the NULL terminator */
233 if (si->Title) strncpy (name, si->Title, 20);
235 if (FWrite (fh, name, 20, 1) != 1) return ERROR_IOERR;
239 /* Write instruments name, length, volume, repeat, replen */
241 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
242 (APTR)MSG_WRITING_INSTINFO, NULL);
245 for ( j = 1 ; j < (((ULONG)saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32) ; j++ )
257 memset (&modinstr, 0, sizeof (modinstr));
259 if (instr = si->Instr[j])
261 /* Some Trackers require the instrument buffer to be
264 strncpy (modinstr.namebuf, instr->Name, 22);
266 /* SoundTracker cannot handle instruments longer than 64K */
267 if (instr->Length > 0xFFFE)
269 modinstr.length = 0x7FFF;
270 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_WARNING,
271 (APTR)MSG_INSTR_TOO_LONG, j);
273 else modinstr.length = instr->Length >> 1;
275 if (!instr->Sample) modinstr.length = 0;
278 if (saver->xmh_ID == ID_SOUNDTRACKER)
279 modinstr.volume = instr->Volume;
281 modinstr.volume = instr->Volume | ((instr->FineTune & 0x0F) << 8);
283 modinstr.repeat = instr->Repeat >> 1;
284 modinstr.replen = instr->Replen >> 1;
285 if (!modinstr.replen) modinstr.replen = 1;
288 if (FWrite (fh, &modinstr, sizeof (modinstr), 1) != 1)
293 /******************************************/
294 /* Put number of positions in song (BYTE) */
295 /******************************************/
297 if (FPutC (fh, songlength) == ENDSTREAMCH)
301 /*****************************************/
302 /* Put number of patterns in song (BYTE) */
303 /*****************************************/
305 switch (saver->xmh_ID)
307 case ID_SOUNDTRACKER:
308 /* SoundTracker stores the number of patterns here */
312 case ID_STARTREKKER4:
313 /* StarTrekker modules store restart value here */
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.
328 if (FPutC (fh, c) == ENDSTREAMCH)
332 /********************/
333 /* Choose module ID */
334 /********************/
343 l = ID_PROTRACKER100;
345 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
346 (APTR)MSG_EXCEEDS_64_PATTS, NULL);
350 case ID_PROTRACKER100:
357 numtracks = si->MaxTracks;
358 l = TakeTrackerIDs[si->MaxTracks - 1];
366 if (pattcount >= maxnumpatt)
368 pattcount = maxnumpatt;
369 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
370 (APTR)MSG_EXCEEDS_MAXPAATTS, maxnumpatt);
374 /************************/
375 /* Write position table */
376 /************************/
380 memset (postable, 0, 128);
382 /* All this is needed because ProTracker has serious
383 * problems dealing with modules whose postable has
384 * references to inexistant patterns.
386 for (i = 0; i < songlength; i++)
388 postable[i] = si->Sequence[i];
390 if (postable[i] >= pattcount)
394 if (FWrite (fh, postable, 1, 128) != 128) return ERROR_IOERR;
398 /*******************/
399 /* Write module ID */
400 /*******************/
402 if (l != ID_SOUNDTRACKER)
403 if (FWrite (fh, &l, 4, 1) != 1) return ERROR_IOERR;
407 /**********************/
408 /* Write pattern data */
409 /**********************/
411 SetGlobalSpeed (si, numtracks);
413 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
414 (APTR)MSG_WRITING_PATTS, NULL);
416 for (i = 0 ; i < pattcount ; i++)
418 struct Note **tracks = si->Patt[i]->Notes;
420 if (xmDisplayProgress (i, pattcount))
423 for (j = 0 ; j < 0x40 ; j++)
425 for (k = 0 ; k < numtracks ; k++)
427 struct Note *note = &tracks[k][j];
430 strow.Note = TrackerNotes[note->Note];
432 /* Translate instr # (high nibble) & effect (low nibble) */
435 if ((c > 0x0F) && (saver->xmh_ID != ID_SOUNDTRACKER))
437 /* Noise/ProTracker stores the high bit of the
438 * instrument number in bit 15 of the note value.
440 strow.Note |= 0x1000;
443 strow.InstEff = (c << 4) | Effects[note->EffNum];
445 /* Copy effect value */
446 strow.EffVal = note->EffVal;
449 if (FWrite (fh, &strow, 4, 1) != 1) return ERROR_IOERR;
454 /* Instruments are written using non buffered I/O because it's
455 * generally more efficient when writing big buffers.
459 /********************/
460 /* Save Instruments */
461 /********************/
463 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
464 (APTR)MSG_WRITING_INSTDATA, NULL);
466 for (i = 1 ; i < (saver->xmh_ID == ID_SOUNDTRACKER ? 16 : 32) ; i++)
470 if (!(instr = si->Instr[i])) continue;
472 len = instr->Length & (~1);
474 /* Adapt instrument to SoundTracker 64K limit */
475 if (len > 0xFFFE) len = 0xFFFE;
477 if (xmDisplayProgress (i, (saver->xmh_ID == ID_SOUNDTRACKER) ? 16 : 32))
481 if (Write (fh, instr->Sample, len) != len)
490 static HOOKCALL LONG LoadTracker (
492 REG(a0, struct SongInfo *si),
493 REG(a1, struct XMHook *loader),
494 REG(a2, ULONG *tags))
497 struct Instrument *instr;
498 struct Pattern *patt;
499 struct StRow *stpatt, /* Temporary storage for a Tracker pattern */
502 ULONG i, j, k; /* Loop counters */
504 UWORD numtracks = 4, numpatterns, songlen;
505 UBYTE c; /* Read buffers */
507 ULONG soundtracker = (loader->xmh_ID == ID_SOUNDTRACKER);
510 /* Get the score name */
514 if (FRead (fh, name, 20, 1) != 1) return ERROR_IOERR;
515 name[20] = '\0'; /* Ensure the string is Null-terminated */
522 /* Get the Instruments name, length, cycle, effects */
524 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
525 (APTR)MSG_READING_INSTS_INFO, NULL);
527 for ( j = 1 ; j < (soundtracker ? 16 : 32); j++ )
540 memset (&modinstr, 0, sizeof (modinstr));
542 if (FRead (fh, &modinstr, sizeof (modinstr), 1) != 1)
545 if (modinstr.namebuf[0] || modinstr.length)
547 modinstr.namebuf[21] = '\0'; /* Ensure the string is Null-terminated */
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),
558 return ERROR_NO_FREE_STORE;
563 /* Read song length */
565 if ((songlen = FGetC (fh)) == ENDSTREAMCH) return ERROR_IOERR;
567 /* Read & Ignore number of song patterns.
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.
578 if (FGetC (fh) == ENDSTREAMCH) return ERROR_IOERR;
581 /* Read the song sequence */
585 if (FRead (fh, postable, 1, 128) != 128) return ERROR_IOERR;
587 if (!(xmSetSongLen (si, songlen)))
588 return ERROR_NO_FREE_STORE;
590 for (i = 0; i < si->Length; i++)
591 si->Sequence[i] = postable[i];
593 /* Search for the highest pattern referenced in the position
594 * table to find out the actual number of patterns.
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.
606 for (i = 0 ; i < 128 ; i++)
607 if (postable[i] > numpatterns)
608 numpatterns = postable[i];
610 numpatterns++; /* Pattern numbering starts from 0 */
615 /* Check module ID */
619 ULONG __aligned id = 0;
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.
625 if (FRead (fh, &id, 4, 1) != 1) return ERROR_IOERR;
628 /* Check for FastTracker/TakeTracker IDs */
630 for (i = 0; i < 32; i++)
632 if (id == TakeTrackerIDs[i])
643 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
644 (APTR)MSG_MODULE_ID, IDtoStr (id, buf));
649 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION,
650 (APTR)MSG_MODULE_HAS_N_CHN, numtracks);
653 /* Allocate memory for a full SoundTracker pattern */
655 si->MaxTracks = numtracks;
656 pattsize = (sizeof (struct StRow) * 0x40) * numtracks;
658 if (!(stpatt = AllocMem (pattsize, 0)))
659 return ERROR_NO_FREE_STORE;
662 /* Read pattern data */
664 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
665 (APTR)MSG_READING_PATTS, NULL);
667 for (i = 0; i < numpatterns; i++)
669 if (xmDisplayProgress (i, numpatterns))
671 FreeMem (stpatt, pattsize);
675 /* Read a whole Tracker row */
676 if (FRead (fh, stpatt, pattsize, 1) != 1)
678 FreeMem (stpatt, pattsize);
682 /* Reset note counter */
685 /* Allocate memory for pattern */
686 if (!(patt = xmAddPattern (si,
687 PATTA_Tracks, numtracks,
691 FreeMem (stpatt, pattsize);
692 return ERROR_NO_FREE_STORE;
695 for ( j = 0 ; j < 0x40 ; j++ )
697 for ( k = 0 ; k < numtracks ; k++, strow++ )
699 /* Get address of the current pattern row */
700 note = &patt->Notes[k][j];
702 /* Decode note (highest nibble is cleared) */
703 note->Note = DecodeNote (strow->Note & 0xFFF, i, j, k);
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 */
711 /* Decode effect (low nibble) */
712 note->EffNum = DecodeEff (strow->InstEff & 0x0F, strow->EffVal);
714 /* Copy effect value */
715 note->EffVal = strow->EffVal;
720 FreeMem (stpatt, pattsize);
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)
731 SNGA_GlobalTempo, 125,
735 /* Load Instruments */
737 xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION,
738 (APTR)MSG_READING_INSTS, NULL);
741 for (j = 1 ; j < (soundtracker ? 16 : 32) ; j++)
745 /* Check if instrument exists */
747 if (!(instr = si->Instr[j])) continue;
748 if (instr->Length == 0) continue;
750 if (xmDisplayProgress (j, soundtracker ? 16 : 32))
753 if (!(sample = (BYTE *) AllocVec (instr->Length, MEMF_SAMPLE)))
754 return ERROR_NO_FREE_STORE;
756 if (FRead (fh, sample, instr->Length, 1) != 1)
760 /* Clear instrument lengths */
761 for (i = j; i < 31; i++)
762 if (si->Instr[i]) si->Instr[i]->Length = 0;
766 xmDisplayMessageA (XMDMF_WARNING | XMDMF_USECATALOG,
767 (APTR)MSG_SONG_HAS_NO_INSTS, NULL);
769 return RETURN_WARN; /* Tell 'em this is a damn song */
775 xmSetInstrument (si, j,
776 INSTRA_Sample, sample,
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);
790 INLINE UWORD DecodeNote (UWORD Note, UWORD Patt, UWORD Line, UWORD Track)
795 ULONG n, mid, low = 1, high = MAXNOTES-1;
797 /* The nice binary search learnt at school ;-) */
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);
806 xmDisplayMessage (XMDMF_USECATALOG | XMDMF_NOTE,
807 (APTR)MSG_INVALID_NOTE, Note, Patt, Track, Line);
813 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval)
817 if (eff == 0 && effval)
818 return (EFF_ARPEGGIO);
820 if (eff == 0x0F) /* Speed */
828 for ( i = 0 ; i < MAXTABLEEFFECTS ; i++ )
829 if (eff == Effects[i])
837 static void SetGlobalSpeed (struct SongInfo *si, UWORD tracks)
839 /* Put a speed command ($F) at the first line played in the song */
841 struct Pattern *patt = si->Patt[si->Sequence[0]];
842 struct Note **pn = patt->Notes;
845 tracks = min (tracks, patt->Tracks);
847 /* Do it only if required */
848 if (si->GlobalSpeed != DEF_SONGSPEED)
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... */
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)
860 pn[i][0].EffNum = EFF_SETSPEED;
861 pn[i][0].EffVal = si->GlobalSpeed;
865 if (si->GlobalTempo != DEF_SONGTEMPO)
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... */
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)
877 pn[i][0].EffNum = EFF_SETTEMPO;
878 pn[i][0].EffVal = si->GlobalTempo;
884 GLOBALCALL void AddTrackerHooks (void)
886 /* Adds Tracker loaders and savers */
888 ProTrackerLoader = xmAddHook (
889 XMHOOK_Type, NT_XMLOADER,
890 XMHOOK_Name, (LONG)"Tracker",
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,
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,
917 XMHOOK_MaxPatterns, 64,
918 XMHOOK_MaxInstruments, 15,
919 XMHOOK_MaxLength, 128,
920 XMHOOK_MaxSampleLen, 65534,
921 XMHOOK_MaxPattLen, 64,
925 XMHOOK_Type, NT_XMSAVER,
926 XMHOOK_Name, (LONG)"TakeTracker",
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,
943 XMHOOK_Type, NT_XMSAVER,
944 XMHOOK_Name, (LONG)"FastTracker1",
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,
953 XMHOOK_MaxPatterns, 128,
954 XMHOOK_MaxInstruments, 31,
955 XMHOOK_MaxLength, 128,
956 XMHOOK_MaxSampleLen, 65534,
957 XMHOOK_MaxPattLen, 64,
961 XMHOOK_Type, NT_XMSAVER,
962 XMHOOK_Name, (LONG)"ProTracker",
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,
971 XMHOOK_MaxPatterns, 64,
972 XMHOOK_MaxInstruments, 31,
973 XMHOOK_MaxLength, 128,
974 XMHOOK_MaxSampleLen, 65534,
975 XMHOOK_MaxPattLen, 64,
979 XMHOOK_Type, NT_XMSAVER,
980 XMHOOK_Name, (LONG)"ProTracker100",
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,
989 XMHOOK_MaxPatterns, 100,
990 XMHOOK_MaxInstruments, 31,
991 XMHOOK_MaxLength, 128,
992 XMHOOK_MaxSampleLen, 65534,
993 XMHOOK_MaxPattLen, 64,
997 XMHOOK_Type, NT_XMSAVER,
998 XMHOOK_Name, (LONG)"NoiseTracker",
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,
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,
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,