--- /dev/null
+/*\r
+\r
+Name:\r
+LOAD_S3M.C\r
+\r
+Description:\r
+Screamtracker (S3M) module loader\r
+\r
+Portability:\r
+All systems - all compilers (hopefully)\r
+\r
+*/\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include "mikmod.h"\r
+\r
+/**************************************************************************\r
+**************************************************************************/\r
+\r
+typedef struct S3MNOTE{\r
+ UBYTE note,ins,vol,cmd,inf;\r
+} S3MNOTE;\r
+\r
+typedef S3MNOTE S3MTRACK[64];\r
+\r
+\r
+/* Raw S3M header struct: */\r
+\r
+typedef struct S3MHEADER{\r
+ char songname[28];\r
+ char t1a;\r
+ char type;\r
+ UBYTE unused1[2];\r
+ UWORD ordnum;\r
+ UWORD insnum;\r
+ UWORD patnum;\r
+ UWORD flags;\r
+ UWORD tracker;\r
+ UWORD fileformat;\r
+ char scrm[4];\r
+ UBYTE mastervol;\r
+ UBYTE initspeed;\r
+ UBYTE inittempo;\r
+ UBYTE mastermult;\r
+ UBYTE ultraclick;\r
+ UBYTE pantable;\r
+ UBYTE unused2[8];\r
+ UWORD special;\r
+ UBYTE channels[32];\r
+} S3MHEADER;\r
+\r
+\r
+/* Raw S3M sampleinfo struct: */\r
+\r
+typedef struct S3MSAMPLE{\r
+ UBYTE type;\r
+ char filename[12];\r
+ UBYTE memsegh;\r
+ UWORD memsegl;\r
+ ULONG length;\r
+ ULONG loopbeg;\r
+ ULONG loopend;\r
+ UBYTE volume;\r
+ UBYTE dsk;\r
+ UBYTE pack;\r
+ UBYTE flags;\r
+ ULONG c2spd;\r
+ UBYTE unused[12];\r
+ char sampname[28];\r
+ char scrs[4];\r
+} S3MSAMPLE;\r
+\r
+\r
+/**************************************************************************\r
+**************************************************************************/\r
+\r
+\r
+\r
+S3MNOTE *s3mbuf; /* pointer to a complete S3M pattern */\r
+UWORD *paraptr; /* parapointer array (see S3M docs) */\r
+static S3MHEADER *mh;\r
+UBYTE remap[32];\r
+\r
+\r
+char S3M_Version[]="Screamtracker 3.xx";\r
+\r
+\r
+\r
+BOOL S3M_Test(void)\r
+{\r
+ char id[4];\r
+ _mm_fseek(modfp,0x2c,SEEK_SET);\r
+ if(!fread(id,4,1,modfp)) return 0;\r
+ if(!memcmp(id,"SCRM",4)) return 1;\r
+ return 0;\r
+}\r
+\r
+BOOL S3M_Init(void)\r
+{\r
+ s3mbuf=NULL;\r
+ paraptr=NULL;\r
+\r
+ if(!(s3mbuf=(S3MNOTE *)MyMalloc(16*64*sizeof(S3MNOTE)))) return 0;\r
+ if(!(mh=(S3MHEADER *)MyCalloc(1,sizeof(S3MHEADER)))) return 0;\r
+\r
+ return 1;\r
+}\r
+\r
+void S3M_Cleanup(void)\r
+{\r
+ if(s3mbuf!=NULL) free(s3mbuf);\r
+ if(paraptr!=NULL) free(paraptr);\r
+ if(mh!=NULL) free(mh);\r
+}\r
+\r
+\r
+\r
+\r
+BOOL S3M_ReadPattern(void)\r
+{\r
+ int row=0,flag,ch;\r
+ S3MNOTE *n;\r
+ S3MNOTE dummy;\r
+\r
+ /* clear pattern data */\r
+\r
+ memset(s3mbuf,255,16*64*sizeof(S3MNOTE));\r
+\r
+ while(row<64){\r
+\r
+ flag=fgetc(modfp);\r
+\r
+ if(flag==EOF){\r
+ myerr="Error loading pattern";\r
+ return 0;\r
+ }\r
+\r
+ if(flag){\r
+\r
+ ch=flag&31;\r
+\r
+ if(mh->channels[ch]<16){\r
+ n=&s3mbuf[(64U*remap[ch])+row];\r
+ }\r
+ else{\r
+ n=&dummy;\r
+ }\r
+\r
+ if(flag&32){\r
+ n->note=fgetc(modfp);\r
+ n->ins=fgetc(modfp);\r
+ }\r
+\r
+ if(flag&64){\r
+ n->vol=fgetc(modfp);\r
+ }\r
+\r
+ if(flag&128){\r
+ n->cmd=fgetc(modfp);\r
+ n->inf=fgetc(modfp);\r
+ }\r
+ }\r
+ else row++;\r
+ }\r
+ return 1;\r
+}\r
+\r
+\r
+\r
+UBYTE *S3M_ConvertTrack(S3MNOTE *tr)\r
+{\r
+ int t;\r
+\r
+ UBYTE note,ins,vol,cmd,inf,lo,hi;\r
+\r
+ UniReset();\r
+ for(t=0;t<64;t++){\r
+\r
+ note=tr[t].note;\r
+ ins=tr[t].ins;\r
+ vol=tr[t].vol;\r
+ cmd=tr[t].cmd;\r
+ inf=tr[t].inf;\r
+ lo=inf&0xf;\r
+ hi=inf>>4;\r
+\r
+\r
+ if(ins!=0 && ins!=255){\r
+ UniInstrument(ins-1);\r
+ }\r
+\r
+ if(note!=255){\r
+ if(note==254) UniPTEffect(0xc,0); /* <- note off command */\r
+ else UniNote(((note>>4)*12)+(note&0xf)); /* <- normal note */\r
+ }\r
+\r
+ if(vol<255){\r
+ UniPTEffect(0xc,vol);\r
+/* UniWrite(UNI_S3MVOLUME); */\r
+/* UniWrite(vol); */\r
+ }\r
+\r
+ if(cmd!=255){\r
+ switch(cmd){\r
+\r
+ case 1: /* Axx set speed to xx */\r
+ UniWrite(UNI_S3MEFFECTA);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 2: /* Bxx position jump */\r
+ UniPTEffect(0xb,inf);\r
+ break;\r
+\r
+ case 3: /* Cxx patternbreak to row xx */\r
+ UniPTEffect(0xd,inf);\r
+ break;\r
+\r
+ case 4: /* Dxy volumeslide */\r
+ UniWrite(UNI_S3MEFFECTD);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 5: /* Exy toneslide down */\r
+ UniWrite(UNI_S3MEFFECTE);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 6: /* Fxy toneslide up */\r
+ UniWrite(UNI_S3MEFFECTF);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 7: /* Gxx Tone portamento,speed xx */\r
+ UniPTEffect(0x3,inf);\r
+ break;\r
+\r
+ case 8: /* Hxy vibrato */\r
+ UniPTEffect(0x4,inf);\r
+ break;\r
+\r
+ case 9: /* Ixy tremor, ontime x, offtime y */\r
+ UniWrite(UNI_S3MEFFECTI);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 0xa: /* Jxy arpeggio */\r
+ UniPTEffect(0x0,inf);\r
+ break;\r
+\r
+ case 0xb: /* Kxy Dual command H00 & Dxy */\r
+ UniPTEffect(0x4,0);\r
+ UniWrite(UNI_S3MEFFECTD);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 0xc: /* Lxy Dual command G00 & Dxy */\r
+ UniPTEffect(0x3,0);\r
+ UniWrite(UNI_S3MEFFECTD);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 0xf: /* Oxx set sampleoffset xx00h */\r
+ UniPTEffect(0x9,inf);\r
+ break;\r
+\r
+ case 0x11: /* Qxy Retrig (+volumeslide) */\r
+ UniWrite(UNI_S3MEFFECTQ);\r
+ UniWrite(inf);\r
+ break;\r
+\r
+ case 0x12: /* Rxy tremolo speed x, depth y */\r
+ UniPTEffect(0x6,inf);\r
+ break;\r
+\r
+ case 0x13: /* Sxx special commands */\r
+ switch(hi){\r
+\r
+ case 0: /* S0x set filter */\r
+ UniPTEffect(0xe,0x00|lo);\r
+ break;\r
+\r
+ case 1: /* S1x set glissando control */\r
+ UniPTEffect(0xe,0x30|lo);\r
+ break;\r
+\r
+ case 2: /* S2x set finetune */\r
+ UniPTEffect(0xe,0x50|lo);\r
+ break;\r
+\r
+ case 3: /* S3x set vibrato waveform */\r
+ UniPTEffect(0xe,0x40|lo);\r
+ break;\r
+\r
+ case 4: /* S4x set tremolo waveform */\r
+ UniPTEffect(0xe,0x70|lo);\r
+ break;\r
+\r
+ case 8: /* S8x set panning position */\r
+ UniPTEffect(0xe,0x80|lo);\r
+ break;\r
+\r
+ case 0xb: /* SBx pattern loop */\r
+ UniPTEffect(0xe,0x60|lo);\r
+ break;\r
+\r
+ case 0xc: /* SCx notecut */\r
+ UniPTEffect(0xe,0xC0|lo);\r
+ break;\r
+\r
+ case 0xd: /* SDx notedelay */\r
+ UniPTEffect(0xe,0xD0|lo);\r
+ break;\r
+\r
+ case 0xe: /* SDx patterndelay */\r
+ UniPTEffect(0xe,0xE0|lo);\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case 0x14: /* Txx tempo */\r
+ if(inf>0x20){\r
+ UniWrite(UNI_S3MEFFECTT);\r
+ UniWrite(inf);\r
+ }\r
+ break;\r
+\r
+ case 0x18: /* Xxx amiga command 8xx */\r
+ UniPTEffect(0x8,inf);\r
+ break;\r
+ }\r
+ }\r
+\r
+ UniNewline();\r
+ }\r
+ return UniDup();\r
+}\r
+\r
+\r
+\r
+\r
+BOOL S3M_Load(void)\r
+{\r
+ int t,u,track=0;\r
+ INSTRUMENT *d;\r
+ SAMPLE *q;\r
+ UBYTE isused[16];\r
+ UBYTE pan[32];\r
+\r
+ /* try to read module header */\r
+\r
+ _mm_read_str(mh->songname,28,modfp);\r
+ mh->t1a =_mm_read_UBYTE(modfp);\r
+ mh->type =_mm_read_UBYTE(modfp);\r
+ _mm_read_UBYTES(mh->unused1,2,modfp);\r
+ mh->ordnum =_mm_read_I_UWORD(modfp);\r
+ mh->insnum =_mm_read_I_UWORD(modfp);\r
+ mh->patnum =_mm_read_I_UWORD(modfp);\r
+ mh->flags =_mm_read_I_UWORD(modfp);\r
+ mh->tracker =_mm_read_I_UWORD(modfp);\r
+ mh->fileformat =_mm_read_I_UWORD(modfp);\r
+ _mm_read_str(mh->scrm,4,modfp);\r
+\r
+ mh->mastervol =_mm_read_UBYTE(modfp);\r
+ mh->initspeed =_mm_read_UBYTE(modfp);\r
+ mh->inittempo =_mm_read_UBYTE(modfp);\r
+ mh->mastermult =_mm_read_UBYTE(modfp);\r
+ mh->ultraclick =_mm_read_UBYTE(modfp);\r
+ mh->pantable =_mm_read_UBYTE(modfp);\r
+ _mm_read_UBYTES(mh->unused2,8,modfp);\r
+ mh->special =_mm_read_I_UWORD(modfp);\r
+ _mm_read_UBYTES(mh->channels,32,modfp);\r
+\r
+ if(feof(modfp)){\r
+ myerr="Error loading header";\r
+ return 0;\r
+ }\r
+\r
+ /* set module variables */\r
+\r
+ of.modtype=strdup(S3M_Version);\r
+ of.songname=DupStr(mh->songname,28); /* make a cstr of songname */\r
+ of.numpat=mh->patnum;\r
+ of.numins=mh->insnum;\r
+ of.initspeed=mh->initspeed;\r
+ of.inittempo=mh->inittempo;\r
+\r
+ /* count the number of channels used */\r
+\r
+ of.numchn=0;\r
+\r
+/* for(t=0;t<32;t++) printf("%2.2x ",mh->channels[t]);\r
+*/\r
+ for(t=0;t<32;t++) remap[t]=0;\r
+ for(t=0;t<16;t++) isused[t]=0;\r
+\r
+ /* set a flag for each channel (1 out of of 16) thats being used: */\r
+\r
+ for(t=0;t<32;t++){\r
+ if(mh->channels[t]<16){\r
+ isused[mh->channels[t]]=1;\r
+ }\r
+ }\r
+\r
+ /* give each of them a different number */\r
+\r
+ for(t=0;t<16;t++){\r
+ if(isused[t]){\r
+ isused[t]=of.numchn;\r
+ of.numchn++;\r
+ }\r
+ }\r
+\r
+ /* build the remap array */\r
+\r
+ for(t=0;t<32;t++){\r
+ if(mh->channels[t]<16){\r
+ remap[t]=isused[mh->channels[t]];\r
+ }\r
+ }\r
+\r
+ /* set panning positions */\r
+\r
+ for(t=0;t<32;t++){\r
+ if(mh->channels[t]<16){\r
+ if(mh->channels[t]<8){\r
+ of.panning[remap[t]]=0x30;\r
+ }\r
+ else{\r
+ of.panning[remap[t]]=0xc0;\r
+ }\r
+ }\r
+ }\r
+\r
+ of.numtrk=of.numpat*of.numchn;\r
+\r
+/* printf("Uses %d channels\n",of.numchn);\r
+*/\r
+ /* read the order data */\r
+\r
+ _mm_read_UBYTES(of.positions,mh->ordnum,modfp);\r
+\r
+ of.numpos=0;\r
+ for(t=0;t<mh->ordnum;t++){\r
+ of.positions[of.numpos]=of.positions[t];\r
+ if(of.positions[t]<254) of.numpos++;\r
+ }\r
+\r
+ if((paraptr=(UWORD *)MyMalloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0;\r
+\r
+ /* read the instrument+pattern parapointers */\r
+\r
+ _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp);\r
+\r
+/* printf("pantab %d\n",mh->pantable);\r
+*/\r
+ if(mh->pantable==252){\r
+\r
+ /* read the panning table */\r
+\r
+ _mm_read_UBYTES(pan,32,modfp);\r
+\r
+ /* set panning positions according to panning table (new for st3.2) */\r
+\r
+ for(t=0;t<32;t++){\r
+ if((pan[t]&0x20) && mh->channels[t]<16){\r
+ of.panning[remap[t]]=(pan[t]&0xf)<<4;\r
+ }\r
+ }\r
+ }\r
+\r
+ /* now is a good time to check if the header was too short :) */\r
+\r
+ if(feof(modfp)){\r
+ myerr="Error loading header";\r
+ return 0;\r
+ }\r
+\r
+ if(!AllocInstruments()) return 0;\r
+\r
+ d=of.instruments;\r
+\r
+ for(t=0;t<of.numins;t++){\r
+ S3MSAMPLE s;\r
+\r
+ d->numsmp=1;\r
+ if(!AllocSamples(d)) return 0;\r
+ q=d->samples;\r
+\r
+ /* seek to instrument position */\r
+\r
+ _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET);\r
+\r
+ /* and load sample info */\r
+\r
+ s.type =_mm_read_UBYTE(modfp);\r
+ _mm_read_str(s.filename,12,modfp);\r
+ s.memsegh =_mm_read_UBYTE(modfp);\r
+ s.memsegl =_mm_read_I_UWORD(modfp);\r
+ s.length =_mm_read_I_ULONG(modfp);\r
+ s.loopbeg =_mm_read_I_ULONG(modfp);\r
+ s.loopend =_mm_read_I_ULONG(modfp);\r
+ s.volume =_mm_read_UBYTE(modfp);\r
+ s.dsk =_mm_read_UBYTE(modfp);\r
+ s.pack =_mm_read_UBYTE(modfp);\r
+ s.flags =_mm_read_UBYTE(modfp);\r
+ s.c2spd =_mm_read_I_ULONG(modfp);\r
+ _mm_read_UBYTES(s.unused,12,modfp);\r
+ _mm_read_str(s.sampname,28,modfp);\r
+ _mm_read_str(s.scrs,4,modfp);\r
+\r
+ if(feof(modfp)){\r
+ myerr=ERROR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+\r
+ d->insname=DupStr(s.sampname,28);\r
+ q->c2spd=s.c2spd;\r
+ q->length=s.length;\r
+ q->loopstart=s.loopbeg;\r
+ q->loopend=s.loopend;\r
+ q->volume=s.volume;\r
+ q->seekpos=(((long)s.memsegh)<<16|s.memsegl)<<4;\r
+\r
+ q->flags=0;\r
+\r
+ if(s.flags&1) q->flags|=SF_LOOP;\r
+ if(s.flags&4) q->flags|=SF_16BITS;\r
+ if(mh->fileformat==1) q->flags|=SF_SIGNED;\r
+\r
+ /* DON'T load sample if it doesn't have the SCRS tag */\r
+\r
+ if(memcmp(s.scrs,"SCRS",4)!=0) q->length=0;\r
+\r
+/* printf("%s\n",d->insname);\r
+*/\r
+ d++;\r
+ }\r
+\r
+ if(!AllocTracks()) return 0;\r
+ if(!AllocPatterns()) return 0;\r
+\r
+ for(t=0;t<of.numpat;t++){\r
+\r
+ /* seek to pattern position ( + 2 skip pattern length ) */\r
+\r
+ _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);\r
+\r
+ if(!S3M_ReadPattern()) return 0;\r
+\r
+ for(u=0;u<of.numchn;u++){\r
+ if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;\r
+ }\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+LOADER load_s3m={\r
+ NULL,\r
+ "S3M",\r
+ "Portable S3M loader v0.2",\r
+ S3M_Init,\r
+ S3M_Test,\r
+ S3M_Load,\r
+ S3M_Cleanup\r
+};\r