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