/* MikMod sound library (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file AUTHORS for complete list. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*============================================================================== $Id$ Screamtracker (S3M) module loader ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_MEMORY_H #include #endif #include #include "mikmod_internals.h" #ifdef SUNOS extern int fprintf(FILE *, const char *, ...); #endif /*========== Module structure */ /* header */ typedef struct S3MHEADER { CHAR songname[28]; UBYTE t1a; UBYTE type; UBYTE unused1[2]; UWORD ordnum; UWORD insnum; UWORD patnum; UWORD flags; UWORD tracker; UWORD fileformat; CHAR scrm[4]; UBYTE mastervol; UBYTE initspeed; UBYTE inittempo; UBYTE mastermult; UBYTE ultraclick; UBYTE pantable; UBYTE unused2[8]; UWORD special; UBYTE channels[32]; } S3MHEADER; /* sample information */ typedef struct S3MSAMPLE { UBYTE type; CHAR filename[12]; UBYTE memsegh; UWORD memsegl; ULONG length; ULONG loopbeg; ULONG loopend; UBYTE volume; UBYTE dsk; UBYTE pack; UBYTE flags; ULONG c2spd; UBYTE unused[12]; CHAR sampname[28]; CHAR scrs[4]; } S3MSAMPLE; typedef struct S3MNOTE { UBYTE note,ins,vol,cmd,inf; } S3MNOTE; /*========== Loader variables */ static S3MNOTE *s3mbuf = NULL; /* pointer to a complete S3M pattern */ static S3MHEADER *mh = NULL; static UWORD *paraptr = NULL; /* parapointer array (see S3M docs) */ static unsigned int tracker; /* tracker id */ /* tracker identifiers */ #define NUMTRACKERS 4 static const CHAR * S3M_Version[] = { "Screamtracker x.xx", "Imago Orpheus x.xx (S3M format)", "Impulse Tracker x.xx (S3M format)", "Unknown tracker x.xx (S3M format)", "Impulse Tracker 2.14p3 (S3M format)", "Impulse Tracker 2.14p4 (S3M format)" }; /* version number position in above array */ static const int numeric[NUMTRACKERS]={14,14,16,16}; /*========== Loader code */ static int S3M_Test(void) { UBYTE id[4]; _mm_fseek(modreader,0x2c,SEEK_SET); if(!_mm_read_UBYTES(id,4,modreader)) return 0; if(!memcmp(id,"SCRM",4)) return 1; return 0; } static int S3M_Init(void) { if(!(s3mbuf=(S3MNOTE*)MikMod_malloc(32*64*sizeof(S3MNOTE)))) return 0; if(!(mh=(S3MHEADER*)MikMod_malloc(sizeof(S3MHEADER)))) return 0; if(!(poslookup=(UBYTE*)MikMod_malloc(sizeof(UBYTE)*256))) return 0; memset(poslookup,-1,256); return 1; } static void S3M_Cleanup(void) { MikMod_free(s3mbuf); MikMod_free(paraptr); MikMod_free(poslookup); MikMod_free(mh); MikMod_free(origpositions); s3mbuf=NULL; paraptr=NULL; poslookup=NULL; mh=NULL; origpositions=NULL; } /* Because so many s3m files have 16 channels as the set number used, but really only use far less (usually 8 to 12 still), I had to make this function, which determines the number of channels that are actually USED by a pattern. For every channel that's used, it sets the appropriate array entry of the global variable 'remap' NOTE: You must first seek to the file location of the pattern before calling this procedure. Returns 0 on fail. */ static int S3M_GetNumChannels(void) { int row=0,flag,ch; while(row<64) { flag=_mm_read_UBYTE(modreader); if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } if(flag) { ch=flag&31; if(mh->channels[ch]<32) remap[ch] = 0; if(flag&32) {_mm_skip_BYTE(modreader);_mm_skip_BYTE(modreader);} if(flag&64) _mm_skip_BYTE(modreader); if(flag&128){_mm_skip_BYTE(modreader);_mm_skip_BYTE(modreader);} } else row++; } return 1; } static int S3M_ReadPattern(void) { int row=0,flag,ch; S3MNOTE *n,dummy; /* clear pattern data */ memset(s3mbuf,255,32*64*sizeof(S3MNOTE)); while(row<64) { flag=_mm_read_UBYTE(modreader); if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } if(flag) { ch=remap[flag&31]; if(ch!=-1) n=&s3mbuf[(64U*ch)+row]; else n=&dummy; if(flag&32) { n->note=_mm_read_UBYTE(modreader); n->ins=_mm_read_UBYTE(modreader); } if(flag&64) { n->vol=_mm_read_UBYTE(modreader); if (n->vol>64) n->vol=64; } if(flag&128) { n->cmd=_mm_read_UBYTE(modreader); n->inf=_mm_read_UBYTE(modreader); } } else row++; } return 1; } static UBYTE* S3M_ConvertTrack(S3MNOTE* tr) { int t; UniReset(); for(t=0;t<64;t++) { UBYTE note,ins,vol; note=tr[t].note; ins=tr[t].ins; vol=tr[t].vol; if((ins)&&(ins!=255)) UniInstrument(ins-1); if(note!=255) { if(note==254) { UniPTEffect(0xc,0); /* note cut command */ vol=255; } else UniNote(((note>>4)*OCTAVE)+(note&0xf)); /* normal note */ } if(vol<255) UniPTEffect(0xc,vol); S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf, tracker == 1 ? S3MIT_OLDSTYLE | S3MIT_SCREAM : S3MIT_OLDSTYLE); UniNewline(); } return UniDup(); } static int S3M_Load(int curious) { int t,u,track = 0; SAMPLE *q; UBYTE pan[32]; /* try to read module header */ _mm_read_string(mh->songname,28,modreader); mh->t1a =_mm_read_UBYTE(modreader); mh->type =_mm_read_UBYTE(modreader); _mm_read_UBYTES(mh->unused1,2,modreader); mh->ordnum =_mm_read_I_UWORD(modreader); mh->insnum =_mm_read_I_UWORD(modreader); mh->patnum =_mm_read_I_UWORD(modreader); mh->flags =_mm_read_I_UWORD(modreader); mh->tracker =_mm_read_I_UWORD(modreader); mh->fileformat =_mm_read_I_UWORD(modreader); _mm_read_string(mh->scrm,4,modreader); mh->mastervol =_mm_read_UBYTE(modreader); mh->initspeed =_mm_read_UBYTE(modreader); mh->inittempo =_mm_read_UBYTE(modreader); mh->mastermult =_mm_read_UBYTE(modreader); mh->ultraclick =_mm_read_UBYTE(modreader); mh->pantable =_mm_read_UBYTE(modreader); _mm_read_UBYTES(mh->unused2,8,modreader); mh->special =_mm_read_I_UWORD(modreader); _mm_read_UBYTES(mh->channels,32,modreader); if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } if(mh->ordnum > 255 || mh->insnum > 255 || mh->patnum > 255) { _mm_errno = MMERR_NOT_A_MODULE; return 0; } /* then we can decide the module type */ tracker=mh->tracker>>12; if((!tracker)||(tracker>=NUMTRACKERS)) tracker=NUMTRACKERS-1; /* unknown tracker */ else { if(mh->tracker>=0x3217) tracker=NUMTRACKERS+1; /* IT 2.14p4 */ else if(mh->tracker>=0x3216) tracker=NUMTRACKERS; /* IT 2.14p3 */ else tracker--; } of.modtype = MikMod_strdup(S3M_Version[tracker]); if(trackertracker>>8) &0xf)+'0'; of.modtype[numeric[tracker]+2] = ((mh->tracker>>4)&0xf)+'0'; of.modtype[numeric[tracker]+3] = ((mh->tracker)&0xf)+'0'; } /* set module variables */ of.songname = DupStr(mh->songname,28,0); of.numpat = mh->patnum; of.reppos = 0; of.numins = of.numsmp = mh->insnum; of.initspeed = mh->initspeed; of.inittempo = mh->inittempo; of.initvolume = mh->mastervol<<1; of.flags |= UF_ARPMEM | UF_PANNING; if((mh->tracker==0x1300)||(mh->flags&64)) of.flags|=UF_S3MSLIDES; of.bpmlimit = 32; /* read the order data */ if(!AllocPositions(mh->ordnum)) return 0; if(!(origpositions=(UWORD*)MikMod_calloc(mh->ordnum,sizeof(UWORD)))) return 0; for(t=0;tordnum;t++) { origpositions[t]=_mm_read_UBYTE(modreader); if((origpositions[t]>=mh->patnum)&&(origpositions[t]<254)) origpositions[t]=255/*mh->patnum-1*/; } if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } poslookupcnt=mh->ordnum; S3MIT_CreateOrders(curious); if(!(paraptr=(UWORD*)MikMod_malloc((of.numins+of.numpat)*sizeof(UWORD)))) return 0; /* read the instrument+pattern parapointers */ _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modreader); if(mh->pantable==252) { /* read the panning table (ST 3.2 addition. See below for further portions of channel panning [past reampper]). */ _mm_read_UBYTES(pan,32,modreader); } if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } /* load samples */ if(!AllocSamples()) return 0; q = of.samples; for(t=0;t 64000 && tracker == 1) s.length = 64000; if(_mm_eof(modreader)) { _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } q->samplename = DupStr(s.sampname,28,0); q->speed = s.c2spd; q->length = s.length; q->loopstart = s.loopbeg; q->loopend = s.loopend; q->volume = s.volume; q->seekpos = (((ULONG)s.memsegh)<<16|s.memsegl)<<4; if(s.flags&1) q->flags |= SF_LOOP; if(s.flags&4) q->flags |= SF_16BITS; if(mh->fileformat==1) q->flags |= SF_SIGNED; /* don't load sample if it doesn't have the SCRS tag */ if(memcmp(s.scrs,"SCRS",4)) q->length = 0; q++; } /* determine the number of channels actually used. */ of.numchn = 0; memset(remap,-1,32*sizeof(UBYTE)); for(t=0;tchannels[t]<32)&&(remap[t]!=-1)) { if(mh->channels[t]<8) of.panning[remap[t]]=0x30; else of.panning[remap[t]]=0xc0; } if(mh->pantable==252) /* set panning positions according to panning table (new for st3.2) */ for(t=0;t<32;t++) if((pan[t]&0x20)&&(mh->channels[t]<32)&&(remap[t]!=-1)) of.panning[remap[t]]=(pan[t]&0xf)<<4; /* load pattern info */ of.numtrk=of.numpat*of.numchn; if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; for(t=0;t