4 ** Copyright (C) 1995,96,97 Bernardo Innocenti
6 ** Load a ScreamTracker 3.01 module with any number of tracks.
7 ** Only sample instruments are supported.
11 #include <exec/memory.h>
12 #include <libraries/xmodule.h>
13 #include <libraries/songclass.h>
15 #include <proto/exec.h>
16 #include <proto/dos.h>
17 #include <proto/intuition.h>
18 #include <proto/xmodule.h>
20 #include "XModulePriv.h"
23 /* Local function prototypes */
25 APTR HOOKCALL _GetEngine (
26 REG(a6, struct Library *MyBase));
27 APTR HOOKCALL _SetupXMHook (
28 REG(a0, struct XModuleBase *_XModuleBase),
29 REG(a6, struct Library *mybase));
30 static HOOKCALL struct XMHook *IdentifyScreamTracker (
32 REG(a0, struct XMHook *loader),
33 REG(a1, ULONG *tags));
34 static HOOKCALL LONG LoadScreamTracker (
36 REG(a0, struct SongInfo *si),
37 REG(a1, struct XMHook *loader),
38 REG(a2, ULONG *tags));
39 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval);
43 #define MAKE_ID(a,b,c,d) \
44 ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))
47 /* Convert an Intel style WORD to Motorola format */
48 #define I2M(x) ( (UWORD) ( (((UWORD)(x)) >> 8) | (((UWORD)(x)) << 8) ) )
50 /* Convert an Intel style LONG to Motorola format */
51 #define I2ML(x) ( I2M((x)>>16) | (I2M((x))<<16) )
53 /* Convert a ParaPointer to a normal file offset */
54 #define PARA(x) (((ULONG)I2M(x)) << 4)
56 /* Convert a 3-byte ParaPointer to a normal file offset */
57 #define PARAL(x) (( (ULONG)x[0]<<16 | (ULONG)x[2]<<8 | (ULONG)x[1] ) << 4)
66 UWORD OrdNum; /* Number of positions */
71 UWORD FileFormat; /* See below */
72 ULONG ID; /* Should be ID_SCRM */
84 /* Values for S3MHeader->FileFormat */
86 #define S3MFF_SIGNEDSAMPLES 1
87 #define S3MFF_UNSIGNEDSAMPLES 2
93 UBYTE Type; /* See ITYPE_#? definitions below. */
95 UBYTE MemSeg[3]; /* :-))) Parapointer to sample data */
101 UBYTE Pack; /* See SPACK_#? definitions below. */
102 UBYTE Flags; /* See SFLAG_#? definitions below. */
107 UBYTE SampleName[28];
108 ULONG SampID; /* Should be ID_SCRS */
112 /* S3M Instrument types */
115 #define ITYPE_SAMPLE 1
116 #define ITYPE_ADLIB_MELODY 2
117 #define ITYPE_ADLIB_SNARE 3
118 #define ITYPE_ADLIB_TOM 4
119 #define ITYPE_ADLIB_CYMBAL 5
120 #define ITYPE_ADLIB_HIHAT 6
123 /* S3M Sample packing */
126 #define SPACK_ADPCM 1 /* Unsupported by ST3.01 */
129 /* S3M Sample Flags */
131 #define SFLAG_STEREO 2 /* Unsupported by ST3.01 */
132 #define SFLAG_16BIT 4 /* Unsupported by ST3.01 */
137 #define ID_S3M MAKE_ID('S','3','M','\0') /* ScreamTracker ID */
138 #define ID_SCRM MAKE_ID('S','C','R','M') /* ScreamTracker Module */
139 #define ID_SCRS MAKE_ID('S','C','R','S') /* ScreamTracker Sample */
140 #define ID_SCRI MAKE_ID('S','C','R','I') /* ScreamTracker Instr */
144 /* Effects conversion table */
145 static const UBYTE Effects[MAXTABLEEFFECTS] =
147 /* S3M XModule Val */
149 0x00, /* Null effect $00 */
151 0x05, /* Portamento Up $01 */
152 0x04, /* Portamento Down $02 */
153 0x06, /* Tone Portamento $03 */
154 0x07, /* Vibrato $04 */
155 0x0B, /* ToneP + VolSl $05 */
156 0x0A, /* Vibra + VolSl $06 */
157 0x08, /* Tremolo $07 */
158 0x00, /* Set Hold/Decay $08 */
159 0x0E, /* Sample Offset $09 */
160 0x03, /* Volume Slide $0A */
161 0x01, /* Position Jump $0B */
162 0x12, /* Set Volume $0C */
163 0x02, /* Pattern break $0D */
165 0x00, /* Set Speed $0F */
166 0x11, /* Set Tempo $10 */
167 0x09, /* Arpeggio $11 */
169 0x03, /* Oktalyzer H */
170 0x03 /* Oktalyzer L */
177 const UBYTE LibName[] = "screamtracker.xmhook";
178 const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
179 const UBYTE LibId[] = "screamtracker.xmhook 1.0 (17.3.96) © 1995-96 by Bernardo Innocenti";
182 /* Get around a SAS/C bug which causes some annoying warnings
183 * with the library bases defined below. The problem happens
184 * when the GST has been compiled with the CODE=FAR switch and
185 * the source is being compiled with CODE=NEAR.
188 #pragma msg 72 ignore push
191 struct ExecBase *SysBase = NULL;
192 struct XModuleBase *XModuleBase = NULL;
193 struct DosLibrary *DOSBase = NULL;
194 struct IntuitionBase *IntuitionBase = NULL;
197 #pragma msg 72 ignore pop
201 static HOOKCALL struct XMHook *IdentifyScreamTracker (
203 REG(a0, struct XMHook *loader),
204 REG(a1, ULONG *tags))
206 /* Determine if the given file is a ScreamTracker 3.0 module.
207 * Note: the file position will be changed on exit.
212 Seek (fh, 0x2C, OFFSET_BEGINNING);
213 if (FRead (fh, &id, 4, 1) != 1)
216 return ((id == ID_SCRM) ? loader : NULL);
221 static HOOKCALL LONG LoadScreamTracker (
223 REG(a0, struct SongInfo *si),
224 REG(a1, struct XMHook *loader),
225 REG(a2, ULONG *tags))
230 struct Pattern *patt;
231 struct S3MHeader s3mhd;
233 ULONG i, j; /* Loop counters */
235 UWORD numchannels = 0, w;
236 LONG l; /* Dummy read buffer */
239 /* Read module header */
240 if (FRead (fh, &s3mhd, sizeof (s3mhd), 1) != 1)
243 /* Fix Intel WORD format */
244 s3mhd.OrdNum = I2M (s3mhd.OrdNum);
245 s3mhd.InsNum = I2M (s3mhd.InsNum);
246 s3mhd.PatNum = I2M (s3mhd.PatNum);
247 s3mhd.Flags = I2M (s3mhd.Flags);
248 s3mhd.CreatedWith = I2M (s3mhd.CreatedWith);
249 s3mhd.FileFormat = I2M (s3mhd.FileFormat);
250 s3mhd.Special = I2M (s3mhd.Special);
253 for (i = 0; i < 32; i++)
254 if (s3mhd.Channels[i] != 255) numchannels++;
256 s3mhd.SongName[27] = '\0'; /* Ensure Null-termination */
259 /* Read the pattern sequence */
261 if (!xmSetSongLen (si, s3mhd.OrdNum))
262 return ERROR_NO_FREE_STORE;
264 for (i = 0; i < s3mhd.OrdNum; i++)
266 if ((l = FGetC (fh)) != -1L)
272 if (s3mhd.OrdNum & 1) FGetC (fh); /* Keep WORD alignament */
276 /*******************/
277 /* Get Instruments */
278 /*******************/
280 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
281 (APTR)MSG_READING_INSTS, NULL);
283 /* Get parapointers to instruments */
285 if (!(ParaInst = AllocVec (s3mhd.InsNum * sizeof (UWORD), MEMF_PUBLIC)))
286 return ERROR_NO_FREE_STORE;
288 if (!(ParaPatt = AllocVec (s3mhd.PatNum * sizeof (UWORD), MEMF_PUBLIC)))
291 return ERROR_NO_FREE_STORE;
294 if (FRead (fh, ParaInst, sizeof (UWORD), s3mhd.InsNum) == s3mhd.InsNum)
296 if (FRead (fh, ParaPatt, sizeof (UWORD), s3mhd.PatNum) == s3mhd.PatNum)
300 /* Note: Need to Flush() here? Hmm... I think not. */
302 for (i = 0; i < s3mhd.InsNum; i++)
304 if (xmDisplayProgress (i + 1, s3mhd.InsNum))
310 if (Seek (fh, PARA(ParaInst[i]), OFFSET_BEGINNING) == -1)
317 struct Instrument *inst;
320 if (Read (fh, &samp, sizeof (samp)) != sizeof (samp))
326 samp.SampleName[27] = '\0'; /* Ensure NULL termination */
330 else if (samp.Type == ITYPE_SAMPLE)
332 if (samp.Pack != SPACK_NONE)
333 xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
334 (APTR)MSG_UNKNOWN_SAMPLE_COMPRESSION, i + 1);
336 if (samp.Flags & SFLAG_STEREO)
337 xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
338 (APTR)MSG_INST_IS_STEREO, i + 1);
340 if (samp.Flags & SFLAG_16BIT)
342 xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
343 (APTR)MSG_INST_IS_16BIT, i + 1);
347 if (!(inst = xmAddInstrument (si, i + 1,
348 INSTRA_Name, samp.SampleName,
349 INSTRA_Length, I2ML(samp.Length),
350 INSTRA_Volume, (samp.Volume > 64) ? 64 : samp.Volume,
351 (samp.Flags & SFLAG_LOOP) ? INSTRA_LoopStart : TAG_IGNORE, I2ML(samp.LoopBeg),
352 (samp.Flags & SFLAG_LOOP) ? INSTRA_LoopEnd : TAG_IGNORE, I2ML(samp.LoopEnd),
354 return ERROR_NO_FREE_STORE;
357 /* Seek where the sample is stored */
358 if (Seek (fh, PARAL(samp.MemSeg), OFFSET_BEGINNING) == -1)
364 /* Allocate memory for sample */
366 if (!(sample = AllocVec (inst->Length, MEMF_SAMPLE)))
368 err = ERROR_NO_FREE_STORE;
372 /* Load sample data */
374 if (Read (fh, sample, inst->Length) != inst->Length)
381 if (s3mhd.FileFormat >= S3MFF_UNSIGNEDSAMPLES)
383 /* unsigned -> signed conversion */
384 for (j = 0; j < inst->Length; j++)
388 xmSetInstrument (si, i,
389 INSTRA_Sample, sample,
392 else if (samp.Type <= ITYPE_ADLIB_HIHAT)
394 static UBYTE *ADLibNames[] =
403 xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
404 (APTR)MSG_ADLIB_INSTR, i + 1, ADLibNames[samp.Type - 2]);
407 xmDisplayMessage (XMDMF_ERROR | XMDMF_USECATALOG,
408 (APTR)MSG_ERR_NOT_A_SAMPLE, i + 1);
412 else err = ERROR_IOERR;
414 else err = ERROR_IOERR;
419 SNGA_Title, s3mhd.SongName,
421 SNGA_GlobalSpeed, s3mhd.InitialSpeed,
422 SNGA_GlobalTempo, s3mhd.InitialTempo,
436 xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_ACTION | XMDMF_USECATALOG,
437 (APTR)MSG_READING_PATTS, NULL);
439 for (i = 0; i < s3mhd.PatNum; i++)
441 if (xmDisplayProgress (i + 1, s3mhd.PatNum))
447 if (Seek (fh, PARA(ParaPatt[i]), OFFSET_BEGINNING) == -1)
454 LONG ch = 0; /* Channel number */
458 if (Read (fh, &w, sizeof (UWORD)) != sizeof (UWORD))
464 if (patt = xmAddPattern (si,
465 PATTA_Tracks, numchannels,
470 for (j = 0; (j < 64) && (ch != -1L); j++)
472 while ((ch = FGetC (fh)) != -1L)
474 if (ch == 0) /* End of row */
477 if ((ch & 31) >= numchannels)
479 xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG,
480 (APTR)MSG_TRACK_OUT_OF_RANGE, ch & 31);
481 note = &patt->Notes[0][j];
484 note = &patt->Notes[ch & 31][j];
486 if (ch & 32) /* Note and instrument follows */
490 note->Note = ((((l >> 4) - 2) * 12) | (l & 0x0F)) + 1;
491 note->Inst = FGetC (fh);
493 if (ch & 64) /* Volume */
496 note->EffNum = EFF_SETVOLUME;
500 if (ch & 128) /* Command and Info */
502 note->EffNum = FGetC (fh);
503 note->EffVal = FGetC (fh);
504 note->EffNum = DecodeEff (note->EffNum, note->EffVal);
517 err = ERROR_NO_FREE_STORE;
532 INLINE UBYTE DecodeEff (UBYTE eff, UBYTE effval)
536 if ((eff == 0) && effval) /* Speed/Tempo */
544 for (i = 0 ; i < MAXTABLEEFFECTS; i++)
545 if (eff == Effects[i])
553 HOOKCALL struct Library * _UserLibInit (REG(a6, struct Library *mybase))
560 HOOKCALL struct Library * _UserLibCleanup (REG(a6, struct Library *mybase))
567 HOOKCALL APTR _GetEngine (REG(a6, struct Library *mybase))
574 HOOKCALL APTR _SetupXMHook (
575 REG(a0, struct XModuleBase *_XModuleBase),
576 REG(a6, struct Library *mybase))
578 if (!(strcmp (_XModuleBase->xm_Library.lib_Node.ln_Name, "xmodule.library")))
580 SysBase = *((struct ExecBase **)4);
581 XModuleBase = _XModuleBase;
582 IntuitionBase = XModuleBase->xm_IntuitionBase;
583 DOSBase = XModuleBase->xm_DOSBase;
586 XMHOOK_Type, NT_XMLOADER,
587 XMHOOK_Name, (LONG)"ScreamTracker",
589 XMHOOK_Descr, (LONG)"ScreamTracker 3.01",
590 XMHOOK_Author, (LONG)"Bernardo Innocenti",
593 XMHOOK_LibraryBase, mybase,
594 XMHOOK_LoadModFunc, LoadScreamTracker,
595 XMHOOK_IdentifyModFunc, IdentifyScreamTracker,
596 XMHOOK_MaxTracks, 16,
597 XMHOOK_MaxPatterns, 100,
598 XMHOOK_MaxInstruments, 99,
599 XMHOOK_MaxLength, 255,
600 XMHOOK_MaxSampleLen, 64000,
601 XMHOOK_MaxPattLen, 64,