Mercurial > ~darius > hgwebdir.cgi > mikmod
diff playercode/load_s3m.c @ 4:5d614bcc4287
Initial entry of mikmod into the CVS tree.
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:08 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_s3m.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,466 @@ +/* + + Name: LOAD_S3M.C + + Description: + Screamtracker (S3M) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + +typedef struct S3MNOTE +{ UBYTE note,ins,vol,cmd,inf; +} S3MNOTE; + +typedef S3MNOTE S3MTRACK[64]; + + +// Raw S3M header struct: + +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; + + +// Raw S3M sampleinfo struct: + +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; + +/************************************************************************** +**************************************************************************/ + + +extern UBYTE *poslookup; // S3M/IT fix - removing blank patterns needs a + // lookup table to fix position-jump commands +extern SBYTE remap[64]; // for removing empty channels + +static S3MNOTE *s3mbuf = NULL; // pointer to a complete S3M pattern +static S3MHEADER *mh = NULL; +static UWORD *paraptr = NULL; // parapointer array (see S3M docs) + +CHAR S3M_Version[] = "Screamtracker 3.xx"; + +BOOL S3M_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modfp,0x2c,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modfp)) return 0; + if(!memcmp(id,"SCRM",4)) return 1; + return 0; +} + +BOOL S3M_Init(void) +{ + if(!(s3mbuf = (S3MNOTE *)_mm_malloc(16*64*sizeof(S3MNOTE)))) return 0; + if(!(mh = (S3MHEADER *)_mm_calloc(1,sizeof(S3MHEADER)))) return 0; + if(!(poslookup = (UBYTE *)_mm_malloc(sizeof(UBYTE)*128))) return 0; + + return 1; +} + +void S3M_Cleanup(void) +{ + if(s3mbuf!=NULL) free(s3mbuf); + if(paraptr!=NULL) free(paraptr); + if(poslookup!=NULL) free(poslookup); + if(mh!=NULL) free(mh); + + paraptr = NULL; + s3mbuf = NULL; + poslookup = NULL; + mh = NULL; +} + + +BOOL S3M_GetNumChannels(void) + +// 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 varialbe 'isused' +// +// NOTE: You must first seek to the file location of the pattern before calling +// this procedure. +// Returns 1 on fail. + +{ + int row=0,flag,ch; + + while(row<64) + { flag = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 1; + } + + if(flag) + { ch = flag&31; + if(mh->channels[ch] < 16) remap[ch] = 0; + + if(flag&32) + { _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + } + + if(flag&64) + _mm_read_UBYTE(modfp); + + if(flag&128) + { _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + } + } else row++; + } + + return 0; +} + + +BOOL S3M_ReadPattern(void) +{ + int row=0,flag,ch; + S3MNOTE *n; + S3MNOTE dummy; + + // clear pattern data + memset(s3mbuf,255,16*64*sizeof(S3MNOTE)); + + while(row<64) + { flag = _mm_read_UBYTE(modfp); + + if(flag==EOF) + { _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(modfp); + n->ins = _mm_read_UBYTE(modfp); + } + + if(flag&64) + n->vol = _mm_read_UBYTE(modfp); + + if(flag&128) + { n->cmd = _mm_read_UBYTE(modfp); + n->inf = _mm_read_UBYTE(modfp); + } + } else row++; + } + return 1; +} + + +void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect); + +UBYTE *S3M_ConvertTrack(S3MNOTE *tr) +{ + int t; + + UBYTE note,ins,vol; + + UniReset(); + for(t=0; t<64; t++) + { + note = tr[t].note; + ins = tr[t].ins; + vol = tr[t].vol; + + + if(ins!=0 && ins!=255) UniInstrument(ins-1); + if(note!=255) + { if(note==254) UniPTEffect(0xc,0); // <- note off command + else UniNote(((note>>4)*12)+(note&0xf)); // <- normal note + } + + if(vol<255) + UniPTEffect(0xc,vol); + + S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,1); + UniNewline(); + } + + return UniDup(); +} + + +BOOL S3M_Load(void) +{ + int t,u,track = 0; + SAMPLE *q; + UBYTE pan[32]; + + // try to read module header + + _mm_read_string(mh->songname,28,modfp); + mh->t1a =_mm_read_UBYTE(modfp); + mh->type =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused1,2,modfp); + mh->ordnum =_mm_read_I_UWORD(modfp); + mh->insnum =_mm_read_I_UWORD(modfp); + mh->patnum =_mm_read_I_UWORD(modfp); + mh->flags =_mm_read_I_UWORD(modfp); + mh->tracker =_mm_read_I_UWORD(modfp); + mh->fileformat =_mm_read_I_UWORD(modfp); + _mm_read_string(mh->scrm,4,modfp); + + mh->mastervol =_mm_read_UBYTE(modfp); + mh->initspeed =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->mastermult =_mm_read_UBYTE(modfp); + mh->ultraclick =_mm_read_UBYTE(modfp); + mh->pantable =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused2,8,modfp); + mh->special =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(mh->channels,32,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.modtype = strdup(S3M_Version); + of.modtype[14] = ((mh->tracker >> 8) &0xf) + 0x30; + of.modtype[16] = ((mh->tracker >> 4)&0xf) + 0x30; + of.modtype[17] = ((mh->tracker)&0xf) + 0x30; + of.songname = DupStr(mh->songname,28); + 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; + + // read the order data + if(!AllocPositions(mh->ordnum)) return 0; + for(t=0; t<mh->ordnum; t++) + of.positions[t] = _mm_read_UBYTE(modfp); + + of.numpos = 0; + for(t=0; t<mh->ordnum; t++) + { of.positions[of.numpos] = of.positions[t]; + poslookup[t] = of.numpos; // bug fix for FREAKY S3Ms + if(of.positions[t]<254) of.numpos++; + } + + if((paraptr=(UWORD *)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0; + + // read the instrument+pattern parapointers + _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp); + + + 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,modfp); + } + + + // now is a good time to check if the header was too short :) + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + + // ============================================== + // Load those darned Samples! (no insts in ST3) + + if(!AllocSamples()) return 0; + + q = of.samples; + + for(t=0; t<of.numins; t++) + { S3MSAMPLE s; + + // seek to instrument position + + _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET); + + // and load sample info + + s.type =_mm_read_UBYTE(modfp); + _mm_read_string(s.filename,12,modfp); + s.memsegh =_mm_read_UBYTE(modfp); + s.memsegl =_mm_read_I_UWORD(modfp); + s.length =_mm_read_I_ULONG(modfp); + s.loopbeg =_mm_read_I_ULONG(modfp); + s.loopend =_mm_read_I_ULONG(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.dsk =_mm_read_UBYTE(modfp); + s.pack =_mm_read_UBYTE(modfp); + s.flags =_mm_read_UBYTE(modfp); + s.c2spd =_mm_read_I_ULONG(modfp); + _mm_read_UBYTES(s.unused,12,modfp); + _mm_read_string(s.sampname,28,modfp); + _mm_read_string(s.scrs,4,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,28); + q->speed = s.c2spd; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->seekpos = (((long)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)!=0) q->length = 0; + + q++; + } + + // ==================================== + // Determine the number of channels actually used. (what ever happened + // to the concept of a single "numchn" variable, eh?! + + of.numchn = 0; + memset(remap,-1,32*sizeof(UBYTE)); + + for(t=0; t<of.numpat; t++) + { // seek to pattern position ( + 2 skip pattern length ) + _mm_fseek(modfp,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET); + if(S3M_GetNumChannels()) return 0; + } + + // build the remap array + for(t=0; t<32; t++) + { if(remap[t]==0) + { remap[t] = of.numchn; + of.numchn++; + } + } + + // ============================================================ + // set panning positions AFTER building remap chart! + + for(t=0; t<32; t++) + { if((mh->channels[t]<16) && (remap[t]!=-1)) + { if(mh->channels[t]<8) + of.panning[remap[t]] = 0x20; // 0x30 = std s3m val + else + of.panning[remap[t]] = 0xd0; // 0xc0 = std s3m val + } + } + + 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]<16) && (remap[t]!=-1)) + of.panning[remap[t]] = (pan[t]&0xf)<<4; + } + } + + + // ============================== + // Load the pattern info now! + + of.numtrk = of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0; t<of.numpat; t++) + { // seek to pattern position ( + 2 skip pattern length ) + _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET); + if(!S3M_ReadPattern()) return 0; + for(u=0; u<of.numchn; u++) + if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0; + } + + return 1; +} + + +CHAR *S3M_LoadTitle(void) +{ + CHAR s[28]; + + _mm_fseek(modfp,0,SEEK_SET); + if(!fread(s,28,1,modfp)) return NULL; + + return(DupStr(s,28)); +} + + +MLOADER load_s3m = +{ NULL, + "S3M", + "S3M loader v0.3", + S3M_Init, + S3M_Test, + S3M_Load, + S3M_Cleanup, + + S3M_LoadTitle +}; +