#include #include #include #include #include #include "mloader.h" #include "munitrk.h" /************************************************************************** **************************************************************************/ typedef struct XMHEADER{ char id[17]; // ID text: 'Extended module: ' char songname[21]; // Module name, padded with zeroes and 0x1a at the end char trackername[20]; // Tracker name UWORD version; // (word) Version number, hi-byte major and low-byte minor ULONG headersize; // Header size UWORD songlength; // (word) Song length (in patten order table) UWORD restart; // (word) Restart position UWORD numchn; // (word) Number of channels (2,4,6,8,10,...,32) UWORD numpat; // (word) Number of patterns (max 256) UWORD numins; // (word) Number of instruments (max 128) UWORD flags; // (word) Flags: bit 0: 0 = Amiga frequency table (see below) 1 = Linear frequency table UWORD tempo; // (word) Default tempo UWORD bpm; // (word) Default BPM UBYTE orders[256]; // (byte) Pattern order table } XMHEADER; typedef struct XMINSTHEADER{ ULONG size; // (dword) Instrument size char name[22]; // (char) Instrument name UBYTE type; // (byte) Instrument type (always 0) UWORD numsmp; // (word) Number of samples in instrument ULONG ssize; // } XMINSTHEADER; typedef struct XMPATCHHEADER{ UBYTE what[96]; // (byte) Sample number for all notes UBYTE volenv[48]; // (byte) Points for volume envelope UBYTE panenv[48]; // (byte) Points for panning envelope UBYTE volpts; // (byte) Number of volume points UBYTE panpts; // (byte) Number of panning points UBYTE volsus; // (byte) Volume sustain point UBYTE volbeg; // (byte) Volume loop start point UBYTE volend; // (byte) Volume loop end point UBYTE pansus; // (byte) Panning sustain point UBYTE panbeg; // (byte) Panning loop start point UBYTE panend; // (byte) Panning loop end point UBYTE volflg; // (byte) Volume type: bit 0: On; 1: Sustain; 2: Loop UBYTE panflg; // (byte) Panning type: bit 0: On; 1: Sustain; 2: Loop UBYTE vibflg; // (byte) Vibrato type UBYTE vibsweep; // (byte) Vibrato sweep UBYTE vibdepth; // (byte) Vibrato depth UBYTE vibrate; // (byte) Vibrato rate UWORD volfade; // (word) Volume fadeout UWORD reserved[11]; // (word) Reserved } XMPATCHHEADER; typedef struct XMWAVHEADER{ ULONG length; // (dword) Sample length ULONG loopstart; // (dword) Sample loop start ULONG looplength; // (dword) Sample loop length UBYTE volume; // (byte) Volume BYTE finetune; // (byte) Finetune (signed byte -128..+127) UBYTE type; // (byte) Type: Bit 0-1: 0 = No loop, 1 = Forward loop, // 2 = Ping-pong loop; // 4: 16-bit sampledata UBYTE panning; // (byte) Panning (0-255) BYTE relnote; // (byte) Relative note number (signed byte) UBYTE reserved; // (byte) Reserved char samplename[22]; // (char) Sample name } XMWAVHEADER; typedef struct XMPATHEADER{ ULONG size; // (dword) Pattern header length UBYTE packing; // (byte) Packing type (always 0) UWORD numrows; // (word) Number of rows in pattern (1..256) UWORD packsize; // (word) Packed patterndata size } XMPATHEADER; typedef struct MTMNOTE{ UBYTE a,b,c; } MTMNOTE; typedef struct XMNOTE{ UBYTE note,ins,vol,eff,dat; }XMNOTE; XMNOTE *xmpat; /************************************************************************** **************************************************************************/ static XMHEADER *mh; char XM_Version[]="XM"; BOOL XM_Test(void) { char id[17]; rewind(modfp); if(!fread(id,17,1,modfp)) return 0; if(!memcmp(id,"Extended Module: ",17)) return 1; return 0; } BOOL XM_Init(void) { mh=NULL; if(!(mh=MyCalloc(1,sizeof(XMHEADER)))) return 0; return 1; } void XM_Cleanup(void) { if(mh!=NULL) free(mh); } void XM_ReadNote(XMNOTE *n) { UBYTE cmp; memset(n,0,sizeof(XMNOTE)); cmp=fgetc(modfp); if(cmp&0x80){ if(cmp&1) n->note=fgetc(modfp); if(cmp&2) n->ins=fgetc(modfp); if(cmp&4) n->vol=fgetc(modfp); if(cmp&8) n->eff=fgetc(modfp); if(cmp&16) n->dat=fgetc(modfp); } else{ n->note=cmp; n->ins=fgetc(modfp); n->vol=fgetc(modfp); n->eff=fgetc(modfp); n->dat=fgetc(modfp); } } UBYTE *XM_Convert(XMNOTE *xmtrack,UWORD rows) { int t; UBYTE note,ins,vol,eff,dat; UniReset(); for(t=0;tnote; ins=xmtrack->ins; vol=xmtrack->vol; eff=xmtrack->eff; dat=xmtrack->dat; if(note!=0){ UniNote(note-1); } if(ins!=0){ UniInstrument(ins-1); } // printf("Vol:%d\n",vol); switch(vol>>4){ case 0x6: // volslide down if(vol&0xf){ UniWrite(UNI_XMEFFECTA); UniWrite(vol&0xf); } break; case 0x7: // volslide up if(vol&0xf){ UniWrite(UNI_XMEFFECTA); UniWrite(vol<<4); } break; /* volume-row fine volume slide is compatible with protracker EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as opposed to 'take the last sliding value'. */ case 0x8: // finevol down UniPTEffect(0xe,0xb0 | (vol&0xf)); break; case 0x9: // finevol up UniPTEffect(0xe,0xa0 | (vol&0xf)); break; case 0xa: // set vibrato speed UniPTEffect(0x4,vol<<4); break; case 0xb: // vibrato UniPTEffect(0x4,vol&0xf); break; case 0xc: // set panning UniPTEffect(0x8,vol<<4); break; case 0xd: // panning slide left // only slide when data nibble not zero: if(vol&0xf){ UniWrite(UNI_XMEFFECTP); UniWrite(vol&0xf); } break; case 0xe: // panning slide right // only slide when data nibble not zero: if(vol&0xf){ UniWrite(UNI_XMEFFECTP); UniWrite(vol<<4); } break; case 0xf: // tone porta UniPTEffect(0x3,vol<<4); break; default: if(vol>=0x10 && vol<=0x50){ UniPTEffect(0xc,vol-0x10); } } // if(eff>0xf) printf("Effect %d",eff); switch(eff){ case 'G'-55: // G - set global volume break; case 'H'-55: // H - global volume slide break; case 'K'-55: // K - keyoff UniNote(96); break; case 'L'-55: // L - set envelope position break; case 'P'-55: // P - panning slide UniWrite(UNI_XMEFFECTP); UniWrite(dat); break; case 'R'-55: // R - multi retrig note UniWrite(UNI_S3MEFFECTQ); UniWrite(dat); break; case 'T'-55: UniPTEffect(0x6,dat); break; case 'X'-55: if((dat>>4)==1){ // X1 extra fine porta up } else{ // X2 extra fine porta down } break; default: if(eff==0xa){ UniWrite(UNI_XMEFFECTA); UniWrite(dat); } else if(eff<=0xf) UniPTEffect(eff,dat); break; } UniNewline(); xmtrack++; } return UniDup(); } BOOL XM_Load(void) { INSTRUMENT *d; SAMPLE *q; int t,u,v,p,numtrk; long next; rewind(modfp); // try to read module header if(!fread(mh,sizeof(XMHEADER),1,modfp)){ myerr=ERROR_LOADING_HEADER; return 0; } /* set module variables */ of.initspeed=mh->tempo; of.inittempo=mh->bpm; of.modtype=DupStr(mh->trackername,20); of.numchn=mh->numchn; of.numpat=mh->numpat; of.numtrk=(UWORD)of.numpat*of.numchn; // get number of channels of.songname=DupStr(mh->songname,20); // make a cstr of songname of.numpos=mh->songlength; // copy the songlength of.numins=mh->numins; of.flags|=UF_XMPERIODS; if(mh->flags&1) of.flags|=UF_LINEAR; memcpy(of.positions,mh->orders,256); of.numpat=0; for(t=0;tof.numpat) of.numpat=of.positions[t]; } of.numpat++; /* printf("Modtype :%s\n",of.modtype); printf("Version :%x\n",mh->version); printf("Song :%s\n",of.songname); printf("Speed :%d,%d\n",of.initspeed,of.inittempo); printf("Channels:%d\n",of.numchn); printf("Numins :%d\n",mh->numins); */ if(!AllocTracks()) return 0; if(!AllocPatterns()) return 0; numtrk=0; for(t=0;tnumpat;t++){ XMPATHEADER ph; // printf("Reading pattern %d\n",t); fread(&ph,sizeof(XMPATHEADER),1,modfp); // printf("headln: %ld\n",ph.size); // printf("numrows: %d\n",ph.numrows); // printf("packsize:%d\n",ph.packsize); of.pattrows[t]=ph.numrows; /* Gr8.. when packsize is 0, don't try to load a pattern.. it's empty. This bug was discovered thanks to Khyron's module.. */ if(!(xmpat=MyCalloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) return 0; if(ph.packsize>0){ for(u=0;uinsname=DupStr(ih.name,22); d->numsmp=ih.numsmp; if(!AllocSamples(d)) return 0; if(ih.numsmp>0){ XMPATCHHEADER pth; XMWAVHEADER wh; fread(&pth,sizeof(XMPATCHHEADER),1,modfp); memcpy(d->samplenumber,pth.what,96); d->volfade=pth.volfade; // printf("Volfade %x\n",d->volfade); memcpy(d->volenv,pth.volenv,48); d->volflg=pth.volflg; d->volsus=pth.volsus; d->volbeg=pth.volbeg; d->volend=pth.volend; d->volpts=pth.volpts; /* printf("volume points : %d\n" "volflg : %d\n" "volbeg : %d\n" "volend : %d\n" "volsus : %d\n", d->volpts, d->volflg, d->volbeg, d->volend, d->volsus); */ // scale volume envelope: for(p=0;pvolpts;p++){ d->volenv[p].val<<=2; // printf("%d,%d,",d->volenv[p].pos,d->volenv[p].val); } memcpy(d->panenv,pth.panenv,48); d->panflg=pth.panflg; d->pansus=pth.pansus; d->panbeg=pth.panbeg; d->panend=pth.panend; d->panpts=pth.panpts; /* printf("Panning points : %d\n" "panflg : %d\n" "panbeg : %d\n" "panend : %d\n" "pansus : %d\n", d->panpts, d->panflg, d->panbeg, d->panend, d->pansus); */ // scale panning envelope: for(p=0;ppanpts;p++){ d->panenv[p].val<<=2; // printf("%d,%d,",d->panenv[p].pos,d->panenv[p].val); } // for(u=0;u<256;u++){ // printf("%2.2x ",fgetc(modfp)); // } next=0; for(u=0;usamples[u]; fread(&wh,sizeof(XMWAVHEADER),1,modfp); // printf("wav %d:%22.22s\n",u,wh.samplename); q->samplename =DupStr(wh.samplename,22); q->length =wh.length; q->loopstart =wh.loopstart; q->loopend =wh.loopstart+wh.looplength; q->volume =wh.volume; q->c2spd =wh.finetune+128; q->transpose =wh.relnote; q->panning =wh.panning; q->seekpos =next; if(wh.type&0x10){ q->length>>=1; q->loopstart>>=1; q->loopend>>=1; } next+=wh.length; // printf("Type %u\n",wh.type); // printf("Trans %d\n",wh.relnote); q->flags|=SF_OWNPAN; if(wh.type&0x3) q->flags|=SF_LOOP; if(wh.type&0x2) q->flags|=SF_BIDI; if(wh.type&0x10) q->flags|=SF_16BITS; q->flags|=SF_DELTA; q->flags|=SF_SIGNED; } for(u=0;usamples[u].seekpos+=ftell(modfp); fseek(modfp,next,SEEK_CUR); } d++; } return 1; } LOADER xmload={ NULL, "XM", "XM loader v0.3 - for your ears only / MikMak", XM_Init, XM_Test, XM_Load, XM_Cleanup };