#include #include #include #include #include "mloader.h" #include "munitrk.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]; char t1a; char type; char 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; char 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; char unused[12]; char sampname[28]; char scrs[4]; } S3MSAMPLE; /************************************************************************** **************************************************************************/ S3MNOTE *s3mbuf; // pointer to a complete S3M pattern UWORD *paraptr; // parapointer array (see S3M docs) static S3MHEADER *mh; UBYTE remap[32]; char S3M_Version[]="Screamtracker 3.xx"; BOOL S3M_Test(void) { char id[4]; fseek(modfp,0x2c,SEEK_SET); if(!fread(id,4,1,modfp)) return 0; if(!memcmp(id,"SCRM",4)) return 1; return 0; } BOOL S3M_Init(void) { s3mbuf=NULL; paraptr=NULL; if(!(s3mbuf=MyMalloc(16*64*sizeof(S3MNOTE)))) return 0; if(!(mh=MyCalloc(1,sizeof(S3MHEADER)))) return 0; return 1; } void S3M_Cleanup(void) { if(s3mbuf!=NULL) free(s3mbuf); if(paraptr!=NULL) free(paraptr); if(mh!=NULL) free(mh); } 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=fgetc(modfp); if(flag==EOF){ myerr="Error loading pattern"; return 0; } if(flag){ ch=flag&31; if(mh->channels[ch]<16){ n=&s3mbuf[(64U*remap[ch])+row]; } else{ n=&dummy; } if(flag&32){ n->note=fgetc(modfp); n->ins=fgetc(modfp); } if(flag&64){ n->vol=fgetc(modfp); } if(flag&128){ n->cmd=fgetc(modfp); n->inf=fgetc(modfp); } } else row++; } return 1; } UBYTE *S3M_ConvertTrack(S3MNOTE *tr) { int t; UBYTE note,ins,vol,cmd,inf,lo,hi; UniReset(); for(t=0;t<64;t++){ note=tr[t].note; ins=tr[t].ins; vol=tr[t].vol; cmd=tr[t].cmd; inf=tr[t].inf; lo=inf&0xf; hi=inf>>4; 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); // UniWrite(UNI_S3MVOLUME); // UniWrite(vol); } if(cmd!=255){ switch(cmd){ case 1: // Axx set speed to xx UniWrite(UNI_S3MEFFECTA); UniWrite(inf); break; case 2: // Bxx position jump UniPTEffect(0xb,inf); break; case 3: // Cxx patternbreak to row xx UniPTEffect(0xd,inf); break; case 4: // Dxy volumeslide UniWrite(UNI_S3MEFFECTD); UniWrite(inf); break; case 5: // Exy toneslide down UniWrite(UNI_S3MEFFECTE); UniWrite(inf); break; case 6: // Fxy toneslide up UniWrite(UNI_S3MEFFECTF); UniWrite(inf); break; case 7: // Gxx Tone portamento,speed xx UniPTEffect(0x3,inf); break; case 8: // Hxy vibrato UniPTEffect(0x4,inf); break; case 9: // Ixy tremor, ontime x, offtime y UniWrite(UNI_S3MEFFECTI); UniWrite(inf); break; case 0xa: // Jxy arpeggio UniPTEffect(0x0,inf); break; case 0xb: // Kxy Dual command H00 & Dxy UniPTEffect(0x4,0); UniWrite(UNI_S3MEFFECTD); UniWrite(inf); break; case 0xc: // Lxy Dual command G00 & Dxy UniPTEffect(0x3,0); UniWrite(UNI_S3MEFFECTD); UniWrite(inf); break; case 0xf: // Oxx set sampleoffset xx00h UniPTEffect(0x9,inf); break; case 0x11: // Qxy Retrig (+volumeslide) UniWrite(UNI_S3MEFFECTQ); UniWrite(inf); break; case 0x12: // Rxy tremolo speed x, depth y UniPTEffect(0x6,inf); break; case 0x13: // Sxx special commands switch(hi){ case 0: // S0x set filter UniPTEffect(0xe,0x00|lo); break; case 1: // S1x set glissando control UniPTEffect(0xe,0x30|lo); break; case 2: // S2x set finetune UniPTEffect(0xe,0x50|lo); break; case 3: // S3x set vibrato waveform UniPTEffect(0xe,0x40|lo); break; case 4: // S4x set tremolo waveform UniPTEffect(0xe,0x70|lo); break; case 8: // S8x set panning position UniPTEffect(0xe,0x80|lo); break; case 0xb: // SBx pattern loop UniPTEffect(0xe,0x60|lo); break; case 0xc: // SCx notecut UniPTEffect(0xe,0xC0|lo); break; case 0xd: // SDx notedelay UniPTEffect(0xe,0xD0|lo); break; case 0xe: // SDx patterndelay UniPTEffect(0xe,0xE0|lo); break; } break; case 0x14: // Txx tempo if(inf>0x20){ UniWrite(UNI_S3MEFFECTT); UniWrite(inf); } break; case 0x18: // Xxx amiga command 8xx UniPTEffect(0x8,inf); break; } } UniNewline(); } return UniDup(); } BOOL S3M_Load(void) { int t,u,track=0; INSTRUMENT *d; SAMPLE *q; UBYTE isused[16]; UBYTE pan[32]; rewind(modfp); // try to read module header if(!fread(mh,sizeof(S3MHEADER),1,modfp)){ myerr="Error loading header"; return 0; } /* set module variables */ of.modtype=strdup(S3M_Version); of.songname=DupStr(mh->songname,28); // make a cstr of songname of.numpat=mh->patnum; of.numins=mh->insnum; of.initspeed=mh->initspeed; of.inittempo=mh->inittempo; // count the number of channels used 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 of of 16) thats being used: for(t=0;t<32;t++){ if(mh->channels[t]<16){ isused[mh->channels[t]]=1; } } // give each of them a different number for(t=0;t<16;t++){ if(isused[t]){ isused[t]=of.numchn; 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){ of.panning[remap[t]]=0x30; } else{ of.panning[remap[t]]=0xc0; } } } of.numtrk=of.numpat*of.numchn; // read the order data if(!fread(of.positions,mh->ordnum,1,modfp)){ myerr="Error loading header"; return 0; } of.numpos=0; for(t=0;tordnum;t++){ of.positions[of.numpos]=of.positions[t]; if(of.positions[t]<254) of.numpos++; } if((paraptr=MyMalloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0; // read the instrument+pattern parapointers !! MOET NOG AANGEPAST WORDEN if(fread(paraptr,2,of.numins+of.numpat,modfp)!=(of.numins+of.numpat)){ myerr="Error loading header"; return 0; } if(mh->pantable==252){ if(!fread(pan,32,1,modfp)){ myerr="Error loading header"; return 0; } // 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){ of.panning[remap[t]]=(pan[t]&0xf)<<4; } } } if(!AllocInstruments()) return 0; d=of.instruments; for(t=0;tnumsmp=1; if(!AllocSamples(d)) return 0; q=d->samples; // seek to instrument position fseek(modfp,((long)paraptr[t])<<4,SEEK_SET); // and load sample info if(fread(&s,sizeof(S3MSAMPLE),1,modfp)!=1){ myerr=ERROR_LOADING_HEADER; return 0; } d->insname=DupStr(s.sampname,28); q->c2spd=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; q->flags=0; 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; // printf("%s\n",s.sampname); d++; } if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; for(t=0;t