1 /* MikMod sound library
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
3 AUTHORS for complete list.
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
23 $Id: load_s3m.c,v 1.3 2005/04/07 19:57:38 realtech Exp $
25 Screamtracker (S3M) module loader
27 ==============================================================================*/
43 #include "mikmod_internals.h"
46 extern int fprintf(FILE *, const char *, ...);
49 /*========== Module structure */
52 typedef struct S3MHEADER
{
75 /* sample information */
76 typedef struct S3MSAMPLE
{
94 typedef struct S3MNOTE
{
95 UBYTE note
,ins
,vol
,cmd
,inf
;
98 /*========== Loader variables */
100 static S3MNOTE
*s3mbuf
= NULL
; /* pointer to a complete S3M pattern */
101 static S3MHEADER
*mh
= NULL
;
102 static UWORD
*paraptr
= NULL
; /* parapointer array (see S3M docs) */
103 static unsigned int tracker
; /* tracker id */
105 /* tracker identifiers */
106 #define NUMTRACKERS 4
107 static CHAR
* S3M_Version
[] = {
108 "Screamtracker x.xx",
109 "Imago Orpheus x.xx (S3M format)",
110 "Impulse Tracker x.xx (S3M format)",
111 "Unknown tracker x.xx (S3M format)",
112 "Impulse Tracker 2.14p3 (S3M format)",
113 "Impulse Tracker 2.14p4 (S3M format)"
115 /* version number position in above array */
116 static int numeric
[NUMTRACKERS
]={14,14,16,16};
118 /*========== Loader code */
124 _mm_fseek(modreader
,0x2c,SEEK_SET
);
125 if(!_mm_read_UBYTES(id
,4,modreader
)) return 0;
126 if(!memcmp(id
,"SCRM",4)) return 1;
132 if(!(s3mbuf
=(S3MNOTE
*)MikMod_malloc(32*64*sizeof(S3MNOTE
)))) return 0;
133 if(!(mh
=(S3MHEADER
*)MikMod_malloc(sizeof(S3MHEADER
)))) return 0;
134 if(!(poslookup
=(UBYTE
*)MikMod_malloc(sizeof(UBYTE
)*256))) return 0;
135 memset(poslookup
,-1,256);
140 void S3M_Cleanup(void)
143 MikMod_free(paraptr
);
144 MikMod_free(poslookup
);
146 MikMod_free(origpositions
);
149 /* Because so many s3m files have 16 channels as the set number used, but really
150 only use far less (usually 8 to 12 still), I had to make this function, which
151 determines the number of channels that are actually USED by a pattern.
153 For every channel that's used, it sets the appropriate array entry of the
154 global variable 'remap'
156 NOTE: You must first seek to the file location of the pattern before calling
159 Returns 1 on fail. */
160 static int S3M_GetNumChannels(void)
165 flag
=_mm_read_UBYTE(modreader
);
167 if(_mm_eof(modreader
)) {
168 _mm_errno
= MMERR_LOADING_PATTERN
;
174 if(mh
->channels
[ch
]<32) remap
[ch
] = 0;
175 if(flag
&32) {(void)_mm_read_UBYTE(modreader
);(void)_mm_read_UBYTE(modreader
);}
176 if(flag
&64) (void)_mm_read_UBYTE(modreader
);
177 if(flag
&128){(void)_mm_read_UBYTE(modreader
);(void)_mm_read_UBYTE(modreader
);}
183 static int S3M_ReadPattern(void)
188 /* clear pattern data */
189 memset(s3mbuf
,255,32*64*sizeof(S3MNOTE
));
192 flag
=_mm_read_UBYTE(modreader
);
194 if(_mm_eof(modreader
)) {
195 _mm_errno
= MMERR_LOADING_PATTERN
;
203 n
=&s3mbuf
[(64U*ch
)+row
];
208 n
->note
=_mm_read_UBYTE(modreader
);
209 n
->ins
=_mm_read_UBYTE(modreader
);
212 n
->vol
=_mm_read_UBYTE(modreader
);
213 if (n
->vol
>64) n
->vol
=64;
216 n
->cmd
=_mm_read_UBYTE(modreader
);
217 n
->inf
=_mm_read_UBYTE(modreader
);
224 static UBYTE
* S3M_ConvertTrack(S3MNOTE
* tr
)
236 if((ins
)&&(ins
!=255)) UniInstrument(ins
-1);
239 UniPTEffect(0xc,0); /* note cut command */
242 UniNote(((note
>>4)*OCTAVE
)+(note
&0xf)); /* normal note */
244 if(vol
<255) UniPTEffect(0xc,vol
);
246 S3MIT_ProcessCmd(tr
[t
].cmd
,tr
[t
].inf
,
247 tracker
== 1 ? S3MIT_OLDSTYLE
| S3MIT_SCREAM
: S3MIT_OLDSTYLE
);
253 int S3M_Load(int curious
)
259 /* try to read module header */
260 _mm_read_string(mh
->songname
,28,modreader
);
261 mh
->t1a
=_mm_read_UBYTE(modreader
);
262 mh
->type
=_mm_read_UBYTE(modreader
);
263 _mm_read_UBYTES(mh
->unused1
,2,modreader
);
264 mh
->ordnum
=_mm_read_I_UWORD(modreader
);
265 mh
->insnum
=_mm_read_I_UWORD(modreader
);
266 mh
->patnum
=_mm_read_I_UWORD(modreader
);
267 mh
->flags
=_mm_read_I_UWORD(modreader
);
268 mh
->tracker
=_mm_read_I_UWORD(modreader
);
269 mh
->fileformat
=_mm_read_I_UWORD(modreader
);
270 _mm_read_string(mh
->scrm
,4,modreader
);
271 mh
->mastervol
=_mm_read_UBYTE(modreader
);
272 mh
->initspeed
=_mm_read_UBYTE(modreader
);
273 mh
->inittempo
=_mm_read_UBYTE(modreader
);
274 mh
->mastermult
=_mm_read_UBYTE(modreader
);
275 mh
->ultraclick
=_mm_read_UBYTE(modreader
);
276 mh
->pantable
=_mm_read_UBYTE(modreader
);
277 _mm_read_UBYTES(mh
->unused2
,8,modreader
);
278 mh
->special
=_mm_read_I_UWORD(modreader
);
279 _mm_read_UBYTES(mh
->channels
,32,modreader
);
281 if(_mm_eof(modreader
)) {
282 _mm_errno
= MMERR_LOADING_HEADER
;
286 /* then we can decide the module type */
287 tracker
=mh
->tracker
>>12;
288 if((!tracker
)||(tracker
>=NUMTRACKERS
))
289 tracker
=NUMTRACKERS
-1; /* unknown tracker */
291 if(mh
->tracker
>=0x3217)
292 tracker
=NUMTRACKERS
+1; /* IT 2.14p4 */
293 else if(mh
->tracker
>=0x3216)
294 tracker
=NUMTRACKERS
; /* IT 2.14p3 */
297 of
.modtype
= StrDup(S3M_Version
[tracker
]);
298 if(tracker
<NUMTRACKERS
) {
299 of
.modtype
[numeric
[tracker
]] = ((mh
->tracker
>>8) &0xf)+'0';
300 of
.modtype
[numeric
[tracker
]+2] = ((mh
->tracker
>>4)&0xf)+'0';
301 of
.modtype
[numeric
[tracker
]+3] = ((mh
->tracker
)&0xf)+'0';
303 /* set module variables */
304 of
.songname
= DupStr(mh
->songname
,28,0);
305 of
.numpat
= mh
->patnum
;
307 of
.numins
= of
.numsmp
= mh
->insnum
;
308 of
.initspeed
= mh
->initspeed
;
309 of
.inittempo
= mh
->inittempo
;
310 of
.initvolume
= mh
->mastervol
<<1;
311 of
.flags
|= UF_ARPMEM
| UF_PANNING
;
312 if((mh
->tracker
==0x1300)||(mh
->flags
&64))
313 of
.flags
|=UF_S3MSLIDES
;
316 /* read the order data */
317 if(!AllocPositions(mh
->ordnum
)) return 0;
318 if(!(origpositions
=MikMod_calloc(mh
->ordnum
,sizeof(UWORD
)))) return 0;
320 for(t
=0;t
<mh
->ordnum
;t
++) {
321 origpositions
[t
]=_mm_read_UBYTE(modreader
);
322 if((origpositions
[t
]>=mh
->patnum
)&&(origpositions
[t
]<254))
323 origpositions
[t
]=255/*mh->patnum-1*/;
326 if(_mm_eof(modreader
)) {
327 _mm_errno
= MMERR_LOADING_HEADER
;
331 poslookupcnt
=mh
->ordnum
;
332 S3MIT_CreateOrders(curious
);
334 if(!(paraptr
=(UWORD
*)MikMod_malloc((of
.numins
+of
.numpat
)*sizeof(UWORD
))))
337 /* read the instrument+pattern parapointers */
338 _mm_read_I_UWORDS(paraptr
,of
.numins
+of
.numpat
,modreader
);
340 if(mh
->pantable
==252) {
341 /* read the panning table (ST 3.2 addition. See below for further
342 portions of channel panning [past reampper]). */
343 _mm_read_UBYTES(pan
,32,modreader
);
346 if(_mm_eof(modreader
)) {
347 _mm_errno
= MMERR_LOADING_HEADER
;
352 if(!AllocSamples()) return 0;
354 for(t
=0;t
<of
.numins
;t
++) {
357 /* seek to instrument position */
358 _mm_fseek(modreader
,((long)paraptr
[t
])<<4,SEEK_SET
);
359 /* and load sample info */
360 s
.type
=_mm_read_UBYTE(modreader
);
361 _mm_read_string(s
.filename
,12,modreader
);
362 s
.memsegh
=_mm_read_UBYTE(modreader
);
363 s
.memsegl
=_mm_read_I_UWORD(modreader
);
364 s
.length
=_mm_read_I_ULONG(modreader
);
365 s
.loopbeg
=_mm_read_I_ULONG(modreader
);
366 s
.loopend
=_mm_read_I_ULONG(modreader
);
367 s
.volume
=_mm_read_UBYTE(modreader
);
368 s
.dsk
=_mm_read_UBYTE(modreader
);
369 s
.pack
=_mm_read_UBYTE(modreader
);
370 s
.flags
=_mm_read_UBYTE(modreader
);
371 s
.c2spd
=_mm_read_I_ULONG(modreader
);
372 _mm_read_UBYTES(s
.unused
,12,modreader
);
373 _mm_read_string(s
.sampname
,28,modreader
);
374 _mm_read_string(s
.scrs
,4,modreader
);
376 /* ScreamTracker imposes a 64000 bytes (not 64k !) limit */
377 if (s
.length
> 64000)
380 if(_mm_eof(modreader
)) {
381 _mm_errno
= MMERR_LOADING_SAMPLEINFO
;
385 q
->samplename
= DupStr(s
.sampname
,28,0);
387 q
->length
= s
.length
;
388 q
->loopstart
= s
.loopbeg
;
389 q
->loopend
= s
.loopend
;
390 q
->volume
= s
.volume
;
391 q
->seekpos
= (((long)s
.memsegh
)<<16|s
.memsegl
)<<4;
393 if(s
.flags
&1) q
->flags
|= SF_LOOP
;
394 if(s
.flags
&4) q
->flags
|= SF_16BITS
;
395 if(mh
->fileformat
==1) q
->flags
|= SF_SIGNED
;
397 /* don't load sample if it doesn't have the SCRS tag */
398 if(memcmp(s
.scrs
,"SCRS",4)) q
->length
= 0;
403 /* determine the number of channels actually used. */
405 memset(remap
,-1,32*sizeof(UBYTE
));
406 for(t
=0;t
<of
.numpat
;t
++) {
407 /* seek to pattern position (+2 skip pattern length) */
408 _mm_fseek(modreader
,(long)((paraptr
[of
.numins
+t
])<<4)+2,SEEK_SET
);
409 if(S3M_GetNumChannels()) return 0;
412 /* build the remap array */
415 remap
[t
]=of
.numchn
++;
417 /* set panning positions after building remap chart! */
419 if((mh
->channels
[t
]<32)&&(remap
[t
]!=-1)) {
420 if(mh
->channels
[t
]<8)
421 of
.panning
[remap
[t
]]=0x30;
423 of
.panning
[remap
[t
]]=0xc0;
425 if(mh
->pantable
==252)
426 /* set panning positions according to panning table (new for st3.2) */
428 if((pan
[t
]&0x20)&&(mh
->channels
[t
]<32)&&(remap
[t
]!=-1))
429 of
.panning
[remap
[t
]]=(pan
[t
]&0xf)<<4;
431 /* load pattern info */
432 of
.numtrk
=of
.numpat
*of
.numchn
;
433 if(!AllocTracks()) return 0;
434 if(!AllocPatterns()) return 0;
436 for(t
=0;t
<of
.numpat
;t
++) {
437 /* seek to pattern position (+2 skip pattern length) */
438 _mm_fseek(modreader
,(((long)paraptr
[of
.numins
+t
])<<4)+2,SEEK_SET
);
439 if(!S3M_ReadPattern()) return 0;
440 for(u
=0;u
<of
.numchn
;u
++)
441 if(!(of
.tracks
[track
++]=S3M_ConvertTrack(&s3mbuf
[u
*64]))) return 0;
447 CHAR
*S3M_LoadTitle(void)
451 _mm_fseek(modreader
,0,SEEK_SET
);
452 if(!_mm_read_UBYTES(s
,28,modreader
)) return NULL
;
454 return(DupStr(s
,28,0));
457 /*========== Loader information */
459 MIKMODAPI MLOADER load_s3m
={
462 "S3M (Scream Tracker 3)",