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