--- /dev/null
+/* MikMod sound library\r
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
+ AUTHORS for complete list.\r
+\r
+ This library is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Library General Public License as\r
+ published by the Free Software Foundation; either version 2 of\r
+ the License, or (at your option) any later version.\r
+ \r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU Library General Public License for more details.\r
+ \r
+ You should have received a copy of the GNU Library General Public\r
+ License along with this library; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+ 02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+ $Id: load_xm.c,v 1.2 2004/02/06 19:29:03 raph Exp $\r
+\r
+ Fasttracker (XM) module loader\r
+\r
+==============================================================================*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#ifdef HAVE_UNISTD_H\r
+#include <unistd.h>\r
+#endif\r
+\r
+#include <stdio.h>\r
+#ifdef HAVE_MEMORY_H\r
+#include <memory.h>\r
+#endif\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+#ifdef SUNOS\r
+extern int fprintf(FILE *, const char *, ...);\r
+#endif\r
+\r
+/*========== Module structure */\r
+\r
+typedef struct XMHEADER {\r
+ CHAR id[17]; /* ID text: 'Extended module: ' */\r
+ CHAR songname[21]; /* Module name */\r
+ CHAR trackername[20]; /* Tracker name */\r
+ UWORD version; /* Version number */\r
+ ULONG headersize; /* Header size */\r
+ UWORD songlength; /* Song length (in patten order table) */\r
+ UWORD restart; /* Restart position */\r
+ UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */\r
+ UWORD numpat; /* Number of patterns (max 256) */\r
+ UWORD numins; /* Number of instruments (max 128) */\r
+ UWORD flags; \r
+ UWORD tempo; /* Default tempo */\r
+ UWORD bpm; /* Default BPM */\r
+ UBYTE orders[256]; /* Pattern order table */\r
+} XMHEADER;\r
+\r
+typedef struct XMINSTHEADER {\r
+ ULONG size; /* Instrument size */\r
+ CHAR name[22]; /* Instrument name */\r
+ UBYTE type; /* Instrument type (always 0) */\r
+ UWORD numsmp; /* Number of samples in instrument */\r
+ ULONG ssize;\r
+} XMINSTHEADER;\r
+\r
+#define XMENVCNT (12*2)\r
+#define XMNOTECNT (8*OCTAVE)\r
+typedef struct XMPATCHHEADER {\r
+ UBYTE what[XMNOTECNT]; /* Sample number for all notes */\r
+ UWORD volenv[XMENVCNT]; /* Points for volume envelope */\r
+ UWORD panenv[XMENVCNT]; /* Points for panning envelope */\r
+ UBYTE volpts; /* Number of volume points */\r
+ UBYTE panpts; /* Number of panning points */\r
+ UBYTE volsus; /* Volume sustain point */\r
+ UBYTE volbeg; /* Volume loop start point */\r
+ UBYTE volend; /* Volume loop end point */\r
+ UBYTE pansus; /* Panning sustain point */\r
+ UBYTE panbeg; /* Panning loop start point */\r
+ UBYTE panend; /* Panning loop end point */\r
+ UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */\r
+ UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */\r
+ UBYTE vibflg; /* Vibrato type */\r
+ UBYTE vibsweep; /* Vibrato sweep */\r
+ UBYTE vibdepth; /* Vibrato depth */\r
+ UBYTE vibrate; /* Vibrato rate */\r
+ UWORD volfade; /* Volume fadeout */\r
+} XMPATCHHEADER;\r
+\r
+typedef struct XMWAVHEADER {\r
+ ULONG length; /* Sample length */\r
+ ULONG loopstart; /* Sample loop start */\r
+ ULONG looplength; /* Sample loop length */\r
+ UBYTE volume; /* Volume */\r
+ SBYTE finetune; /* Finetune (signed byte -128..+127) */\r
+ UBYTE type; /* Loop type */\r
+ UBYTE panning; /* Panning (0-255) */\r
+ SBYTE relnote; /* Relative note number (signed byte) */\r
+ UBYTE reserved;\r
+ CHAR samplename[22]; /* Sample name */\r
+ UBYTE vibtype; /* Vibrato type */\r
+ UBYTE vibsweep; /* Vibrato sweep */\r
+ UBYTE vibdepth; /* Vibrato depth */\r
+ UBYTE vibrate; /* Vibrato rate */\r
+} XMWAVHEADER;\r
+\r
+typedef struct XMPATHEADER {\r
+ ULONG size; /* Pattern header length */\r
+ UBYTE packing; /* Packing type (always 0) */\r
+ UWORD numrows; /* Number of rows in pattern (1..256) */\r
+ SWORD packsize; /* Packed patterndata size */\r
+} XMPATHEADER;\r
+\r
+typedef struct XMNOTE {\r
+ UBYTE note,ins,vol,eff,dat;\r
+} XMNOTE;\r
+\r
+/*========== Loader variables */\r
+\r
+static XMNOTE *xmpat=NULL;\r
+static XMHEADER *mh=NULL;\r
+\r
+/* increment unit for sample array reallocation */\r
+#define XM_SMPINCR 64\r
+static ULONG *nextwav=NULL;\r
+static XMWAVHEADER *wh=NULL,*s=NULL;\r
+\r
+/*========== Loader code */\r
+\r
+BOOL XM_Test(void)\r
+{\r
+ UBYTE id[38];\r
+\r
+ if(!_mm_read_UBYTES(id,38,modreader)) return 0;\r
+ if(memcmp(id,"Extended Module: ",17)) return 0;\r
+ if(id[37]==0x1a) return 1;\r
+ return 0;\r
+}\r
+\r
+BOOL XM_Init(void)\r
+{\r
+ if(!(mh=(XMHEADER *)_mm_malloc(sizeof(XMHEADER)))) return 0;\r
+ return 1;\r
+}\r
+\r
+void XM_Cleanup(void)\r
+{\r
+ _mm_free(mh);\r
+}\r
+\r
+static int XM_ReadNote(XMNOTE* n)\r
+{\r
+ UBYTE cmp,result=1;\r
+\r
+ memset(n,0,sizeof(XMNOTE));\r
+ cmp=_mm_read_UBYTE(modreader);\r
+\r
+ if(cmp&0x80) {\r
+ if(cmp&1) { result++;n->note = _mm_read_UBYTE(modreader); }\r
+ if(cmp&2) { result++;n->ins = _mm_read_UBYTE(modreader); }\r
+ if(cmp&4) { result++;n->vol = _mm_read_UBYTE(modreader); }\r
+ if(cmp&8) { result++;n->eff = _mm_read_UBYTE(modreader); }\r
+ if(cmp&16) { result++;n->dat = _mm_read_UBYTE(modreader); }\r
+ } else {\r
+ n->note = cmp;\r
+ n->ins = _mm_read_UBYTE(modreader);\r
+ n->vol = _mm_read_UBYTE(modreader);\r
+ n->eff = _mm_read_UBYTE(modreader);\r
+ n->dat = _mm_read_UBYTE(modreader);\r
+ result += 4;\r
+ }\r
+ return result;\r
+}\r
+\r
+static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows)\r
+{\r
+ int t;\r
+ UBYTE note,ins,vol,eff,dat;\r
+\r
+ UniReset();\r
+ for(t=0;t<rows;t++) {\r
+ note = xmtrack->note;\r
+ ins = xmtrack->ins;\r
+ vol = xmtrack->vol;\r
+ eff = xmtrack->eff;\r
+ dat = xmtrack->dat;\r
+\r
+ if(note) {\r
+ if(note>XMNOTECNT)\r
+ UniEffect(UNI_KEYFADE,0);\r
+ else\r
+ UniNote(note-1);\r
+ }\r
+ if(ins) UniInstrument(ins-1);\r
+\r
+ switch(vol>>4) {\r
+ case 0x6: /* volslide down */\r
+ if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf);\r
+ break;\r
+ case 0x7: /* volslide up */\r
+ if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4);\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
+ case 0x8: /* finevol down */\r
+ UniPTEffect(0xe,0xb0|(vol&0xf));\r
+ break;\r
+ case 0x9: /* finevol up */\r
+ UniPTEffect(0xe,0xa0|(vol&0xf));\r
+ break;\r
+ case 0xa: /* set vibrato speed */\r
+ UniEffect(UNI_XMEFFECT4,vol<<4);\r
+ break;\r
+ case 0xb: /* vibrato */\r
+ UniEffect(UNI_XMEFFECT4,vol&0xf);\r
+ break;\r
+ case 0xc: /* set panning */\r
+ UniPTEffect(0x8,vol<<4);\r
+ break;\r
+ case 0xd: /* panning slide left (only slide when data not zero) */\r
+ if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf);\r
+ break;\r
+ case 0xe: /* panning slide right (only slide when data not zero) */\r
+ if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4);\r
+ break;\r
+ case 0xf: /* tone porta */\r
+ UniPTEffect(0x3,vol<<4);\r
+ break;\r
+ default:\r
+ if((vol>=0x10)&&(vol<=0x50))\r
+ UniPTEffect(0xc,vol-0x10);\r
+ }\r
+\r
+ switch(eff) {\r
+ case 0x4:\r
+ UniEffect(UNI_XMEFFECT4,dat);\r
+ break;\r
+ case 0x6:\r
+ UniEffect(UNI_XMEFFECT6,dat);\r
+ break;\r
+ case 0xa:\r
+ UniEffect(UNI_XMEFFECTA,dat);\r
+ break;\r
+ case 0xe: /* Extended effects */\r
+ switch(dat>>4) {\r
+ case 0x1: /* XM fine porta up */\r
+ UniEffect(UNI_XMEFFECTE1,dat&0xf);\r
+ break;\r
+ case 0x2: /* XM fine porta down */\r
+ UniEffect(UNI_XMEFFECTE2,dat&0xf);\r
+ break;\r
+ case 0xa: /* XM fine volume up */\r
+ UniEffect(UNI_XMEFFECTEA,dat&0xf);\r
+ break;\r
+ case 0xb: /* XM fine volume down */\r
+ UniEffect(UNI_XMEFFECTEB,dat&0xf);\r
+ break;\r
+ default:\r
+ UniPTEffect(eff,dat);\r
+ }\r
+ break;\r
+ case 'G'-55: /* G - set global volume */\r
+ UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1);\r
+ break;\r
+ case 'H'-55: /* H - global volume slide */\r
+ UniEffect(UNI_XMEFFECTH,dat);\r
+ break;\r
+ case 'K'-55: /* K - keyOff and KeyFade */\r
+ UniEffect(UNI_KEYFADE,dat);\r
+ break;\r
+ case 'L'-55: /* L - set envelope position */\r
+ UniEffect(UNI_XMEFFECTL,dat);\r
+ break;\r
+ case 'P'-55: /* P - panning slide */\r
+ UniEffect(UNI_XMEFFECTP,dat);\r
+ break;\r
+ case 'R'-55: /* R - multi retrig note */\r
+ UniEffect(UNI_S3MEFFECTQ,dat);\r
+ break;\r
+ case 'T'-55: /* T - Tremor */\r
+ UniEffect(UNI_S3MEFFECTI,dat);\r
+ break;\r
+ case 'X'-55:\r
+ switch(dat>>4) {\r
+ case 1: /* X1 - Extra Fine Porta up */\r
+ UniEffect(UNI_XMEFFECTX1,dat&0xf);\r
+ break;\r
+ case 2: /* X2 - Extra Fine Porta down */\r
+ UniEffect(UNI_XMEFFECTX2,dat&0xf);\r
+ break;\r
+ }\r
+ break;\r
+ default:\r
+ if(eff<=0xf) {\r
+ /* the pattern jump destination is written in decimal,\r
+ but it seems some poor tracker software writes them\r
+ in hexadecimal... (sigh) */\r
+ if (eff==0xd)\r
+ /* don't change anything if we're sure it's in hexa */\r
+ if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9))\r
+ /* otherwise, convert from dec to hex */\r
+ dat=(((dat&0xf0)>>4)*10)+(dat&0xf);\r
+ UniPTEffect(eff,dat);\r
+ }\r
+ break;\r
+ }\r
+ UniNewline();\r
+ xmtrack++;\r
+ }\r
+ return UniDup();\r
+}\r
+\r
+static BOOL LoadPatterns(BOOL dummypat)\r
+{\r
+ int t,u,v,numtrk;\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
+ ph.size =_mm_read_I_ULONG(modreader);\r
+ if (ph.size<(mh->version==0x0102?8:9)) {\r
+ _mm_errno=MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+ ph.packing =_mm_read_UBYTE(modreader);\r
+ if(ph.packing) {\r
+ _mm_errno=MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+ if(mh->version==0x0102)\r
+ ph.numrows =_mm_read_UBYTE(modreader)+1;\r
+ else\r
+ ph.numrows =_mm_read_I_UWORD(modreader);\r
+ ph.packsize =_mm_read_I_UWORD(modreader);\r
+\r
+ ph.size-=(mh->version==0x0102?8:9);\r
+ if(ph.size)\r
+ _mm_fseek(modreader,ph.size,SEEK_CUR);\r
+\r
+ of.pattrows[t]=ph.numrows;\r
+\r
+ if(ph.numrows) {\r
+ if(!(xmpat=(XMNOTE*)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE))))\r
+ return 0;\r
+\r
+ /* when packsize is 0, don't try to load a pattern.. it's empty. */\r
+ if(ph.packsize) \r
+ for(u=0;u<ph.numrows;u++) \r
+ for(v=0;v<of.numchn;v++) {\r
+ if(!ph.packsize) break;\r
+\r
+ ph.packsize-=XM_ReadNote(&xmpat[(v*ph.numrows)+u]);\r
+ if(ph.packsize<0) {\r
+ free(xmpat);xmpat=NULL;\r
+ _mm_errno=MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ if(ph.packsize) {\r
+ _mm_fseek(modreader,ph.packsize,SEEK_CUR);\r
+ }\r
+\r
+ if(_mm_eof(modreader)) {\r
+ free(xmpat);xmpat=NULL;\r
+ _mm_errno=MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+\r
+ for(v=0;v<of.numchn;v++)\r
+ of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);\r
+\r
+ free(xmpat);xmpat=NULL;\r
+ } else {\r
+ for(v=0;v<of.numchn;v++)\r
+ of.tracks[numtrk++]=XM_Convert(NULL,ph.numrows);\r
+ }\r
+ }\r
+\r
+ if(dummypat) {\r
+ of.pattrows[t]=64;\r
+ if(!(xmpat=(XMNOTE*)_mm_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;\r
+ for(v=0;v<of.numchn;v++)\r
+ of.tracks[numtrk++]=XM_Convert(&xmpat[v*64],64);\r
+ free(xmpat);xmpat=NULL;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+static void FixEnvelope(ENVPT *cur, int pts)\r
+{\r
+ int u, old, tmp;\r
+ ENVPT *prev;\r
+\r
+ /* Some broken XM editing program will only save the low byte\r
+ of the position value. Try to compensate by adding the\r
+ missing high byte. */\r
+\r
+ prev = cur++;\r
+ old = prev->pos;\r
+\r
+ for (u = 1; u < pts; u++, prev++, cur++) {\r
+ if (cur->pos < prev->pos) {\r
+ if (cur->pos < 0x100) {\r
+ if (cur->pos > old) /* same hex century */\r
+ tmp = cur->pos + (prev->pos - old);\r
+ else\r
+ tmp = cur->pos | ((prev->pos + 0x100) & 0xff00);\r
+ old = cur->pos;\r
+ cur->pos = tmp;\r
+#ifdef MIKMOD_DEBUG\r
+ fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d -> %d\n",\r
+ u, pts, prev->pos, old, cur->pos);\r
+#endif\r
+ } else {\r
+#ifdef MIKMOD_DEBUG\r
+ /* different brokenness style... fix unknown */\r
+ fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d\n",\r
+ u, pts, old, cur->pos);\r
+#endif\r
+ old = cur->pos;\r
+ }\r
+ } else\r
+ old = cur->pos;\r
+ }\r
+}\r
+\r
+static BOOL LoadInstruments(void)\r
+{\r
+ int t,u;\r
+ INSTRUMENT *d;\r
+ ULONG next=0;\r
+ UWORD wavcnt=0;\r
+\r
+ if(!AllocInstruments()) return 0;\r
+ d=of.instruments;\r
+ for(t=0;t<of.numins;t++,d++) {\r
+ XMINSTHEADER ih;\r
+ long headend;\r
+\r
+ memset(d->samplenumber,0xff,INSTNOTES*sizeof(UWORD));\r
+\r
+ /* read instrument header */\r
+ headend = _mm_ftell(modreader);\r
+ ih.size = _mm_read_I_ULONG(modreader);\r
+ headend += ih.size;\r
+ _mm_read_string(ih.name, 22, modreader);\r
+ ih.type = _mm_read_UBYTE(modreader);\r
+ ih.numsmp = _mm_read_I_UWORD(modreader);\r
+\r
+ d->insname = DupStr(ih.name,22,1);\r
+\r
+ if((SWORD)ih.size>29) {\r
+ ih.ssize = _mm_read_I_ULONG(modreader);\r
+ if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) {\r
+ XMPATCHHEADER pth;\r
+ int p;\r
+\r
+ _mm_read_UBYTES (pth.what,XMNOTECNT,modreader);\r
+ _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modreader);\r
+ _mm_read_I_UWORDS (pth.panenv, XMENVCNT, modreader);\r
+ pth.volpts = _mm_read_UBYTE(modreader);\r
+ pth.panpts = _mm_read_UBYTE(modreader);\r
+ pth.volsus = _mm_read_UBYTE(modreader);\r
+ pth.volbeg = _mm_read_UBYTE(modreader);\r
+ pth.volend = _mm_read_UBYTE(modreader);\r
+ pth.pansus = _mm_read_UBYTE(modreader);\r
+ pth.panbeg = _mm_read_UBYTE(modreader);\r
+ pth.panend = _mm_read_UBYTE(modreader);\r
+ pth.volflg = _mm_read_UBYTE(modreader);\r
+ pth.panflg = _mm_read_UBYTE(modreader);\r
+ pth.vibflg = _mm_read_UBYTE(modreader);\r
+ pth.vibsweep = _mm_read_UBYTE(modreader);\r
+ pth.vibdepth = _mm_read_UBYTE(modreader);\r
+ pth.vibrate = _mm_read_UBYTE(modreader);\r
+ pth.volfade = _mm_read_I_UWORD(modreader);\r
+\r
+ /* read the remainder of the header\r
+ (2 bytes for 1.03, 22 for 1.04) */\r
+ for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);\r
+\r
+ /* we can't trust the envelope point count here, as some\r
+ modules have incorrect values (K_OSPACE.XM reports 32 volume\r
+ points, for example). */\r
+ if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2;\r
+ if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2;\r
+\r
+ if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) {\r
+ if(nextwav) { free(nextwav);nextwav=NULL; }\r
+ if(wh) { free(wh);wh=NULL; }\r
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+ return 0;\r
+ }\r
+\r
+ for(u=0;u<XMNOTECNT;u++)\r
+ d->samplenumber[u]=pth.what[u]+of.numsmp;\r
+ d->volfade = pth.volfade;\r
+\r
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C\r
+#define XM_ProcessEnvelope(name) \\r
+ for (u = 0; u < (XMENVCNT >> 1); u++) { \\r
+ d-> name##env[u].pos = pth. name##env[u << 1]; \\r
+ d-> name##env[u].val = pth. name##env[(u << 1)+ 1]; \\r
+ } \\r
+ if (pth. name##flg&1) d-> name##flg|=EF_ON; \\r
+ if (pth. name##flg&2) d-> name##flg|=EF_SUSTAIN; \\r
+ if (pth. name##flg&4) d-> name##flg|=EF_LOOP; \\r
+ d-> name##susbeg=d-> name##susend=pth. name##sus; \\r
+ d-> name##beg=pth. name##beg; \\r
+ d-> name##end=pth. name##end; \\r
+ d-> name##pts=pth. name##pts; \\r
+ \\r
+ /* scale envelope */ \\r
+ for (p=0;p<XMENVCNT/2;p++) \\r
+ d-> name##env[p].val<<=2; \\r
+ \\r
+ if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \\r
+ d-> name##flg&=~EF_ON\r
+#else\r
+#define XM_ProcessEnvelope(name) \\r
+ for (u = 0; u < (XMENVCNT >> 1); u++) { \\r
+ d-> name/**/env[u].pos = pth. name/**/env[u << 1]; \\r
+ d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \\r
+ } \\r
+ if (pth. name/**/flg&1) d-> name/**/flg|=EF_ON; \\r
+ if (pth. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN; \\r
+ if (pth. name/**/flg&4) d-> name/**/flg|=EF_LOOP; \\r
+ d-> name/**/susbeg=d-> name/**/susend= \\r
+ pth. name/**/sus; \\r
+ d-> name/**/beg=pth. name/**/beg; \\r
+ d-> name/**/end=pth. name/**/end; \\r
+ d-> name/**/pts=pth. name/**/pts; \\r
+ \\r
+ /* scale envelope */ \\r
+ for (p=0;p<XMENVCNT/2;p++) \\r
+ d-> name/**/env[p].val<<=2; \\r
+ \\r
+ if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \\r
+ d-> name/**/flg&=~EF_ON\r
+#endif \r
+\r
+ XM_ProcessEnvelope(vol);\r
+ XM_ProcessEnvelope(pan);\r
+#undef XM_ProcessEnvelope\r
+\r
+ if (d->volflg & EF_ON)\r
+ FixEnvelope(d->volenv, d->volpts);\r
+ if (d->panflg & EF_ON)\r
+ FixEnvelope(d->panenv, d->panpts);\r
+\r
+ /* Samples are stored outside the instrument struct now, so we\r
+ have to load them all into a temp area, count the of.numsmp\r
+ along the way and then do an AllocSamples() and move\r
+ everything over */\r
+ if(mh->version>0x0103) next = 0;\r
+ for(u=0;u<ih.numsmp;u++,s++) {\r
+ /* Allocate more room for sample information if necessary */\r
+ if(of.numsmp+u==wavcnt) {\r
+ wavcnt+=XM_SMPINCR;\r
+ if(!(nextwav=realloc(nextwav,wavcnt*sizeof(ULONG)))){\r
+ if(wh) { free(wh);wh=NULL; }\r
+ _mm_errno = MMERR_OUT_OF_MEMORY;\r
+ return 0;\r
+ }\r
+ if(!(wh=realloc(wh,wavcnt*sizeof(XMWAVHEADER)))) {\r
+ free(nextwav);nextwav=NULL;\r
+ _mm_errno = MMERR_OUT_OF_MEMORY;\r
+ return 0;\r
+ }\r
+ s=wh+(wavcnt-XM_SMPINCR);\r
+ }\r
+\r
+ s->length =_mm_read_I_ULONG (modreader);\r
+ s->loopstart =_mm_read_I_ULONG (modreader);\r
+ s->looplength =_mm_read_I_ULONG (modreader);\r
+ s->volume =_mm_read_UBYTE (modreader);\r
+ s->finetune =_mm_read_SBYTE (modreader);\r
+ s->type =_mm_read_UBYTE (modreader);\r
+ s->panning =_mm_read_UBYTE (modreader);\r
+ s->relnote =_mm_read_SBYTE (modreader);\r
+ s->vibtype = pth.vibflg;\r
+ s->vibsweep = pth.vibsweep;\r
+ s->vibdepth = pth.vibdepth*4;\r
+ s->vibrate = pth.vibrate;\r
+ s->reserved =_mm_read_UBYTE (modreader);\r
+ _mm_read_string(s->samplename, 22, modreader);\r
+\r
+ nextwav[of.numsmp+u]=next;\r
+ next+=s->length;\r
+\r
+ if(_mm_eof(modreader)) {\r
+ free(nextwav);free(wh);\r
+ nextwav=NULL;wh=NULL;\r
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ if(mh->version>0x0103) {\r
+ for(u=0;u<ih.numsmp;u++)\r
+ nextwav[of.numsmp++]+=_mm_ftell(modreader);\r
+ _mm_fseek(modreader,next,SEEK_CUR);\r
+ } else\r
+ of.numsmp+=ih.numsmp;\r
+ } else {\r
+ /* read the remainder of the header */\r
+ for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);\r
+\r
+ if(_mm_eof(modreader)) {\r
+ free(nextwav);free(wh);\r
+ nextwav=NULL;wh=NULL;\r
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /* sanity check */\r
+ if(!of.numsmp) {\r
+ if(nextwav) { free(nextwav);nextwav=NULL; }\r
+ if(wh) { free(wh);wh=NULL; }\r
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+ return 0;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+BOOL XM_Load(BOOL curious)\r
+{\r
+ INSTRUMENT *d;\r
+ SAMPLE *q;\r
+ int t,u;\r
+ BOOL dummypat=0;\r
+ char tracker[21],modtype[60];\r
+ (void)curious;\r
+\r
+ /* try to read module header */\r
+ _mm_read_string(mh->id,17,modreader);\r
+ _mm_read_string(mh->songname,21,modreader);\r
+ _mm_read_string(mh->trackername,20,modreader);\r
+ mh->version =_mm_read_I_UWORD(modreader);\r
+ if((mh->version<0x102)||(mh->version>0x104)) {\r
+ _mm_errno=MMERR_NOT_A_MODULE;\r
+ return 0;\r
+ }\r
+ mh->headersize =_mm_read_I_ULONG(modreader);\r
+ mh->songlength =_mm_read_I_UWORD(modreader);\r
+ mh->restart =_mm_read_I_UWORD(modreader);\r
+ mh->numchn =_mm_read_I_UWORD(modreader);\r
+ mh->numpat =_mm_read_I_UWORD(modreader);\r
+ mh->numins =_mm_read_I_UWORD(modreader);\r
+ mh->flags =_mm_read_I_UWORD(modreader);\r
+ mh->tempo =_mm_read_I_UWORD(modreader);\r
+ mh->bpm =_mm_read_I_UWORD(modreader);\r
+ if(!mh->bpm) {\r
+ _mm_errno=MMERR_NOT_A_MODULE;\r
+ return 0;\r
+ }\r
+ _mm_read_UBYTES(mh->orders,256,modreader);\r
+\r
+ if(_mm_eof(modreader)) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+\r
+ /* set module variables */\r
+ of.initspeed = mh->tempo; \r
+ of.inittempo = mh->bpm;\r
+ strncpy(tracker,mh->trackername,20);tracker[20]=0;\r
+ for(t=20;(tracker[t]<=' ')&&(t>=0);t--) tracker[t]=0;\r
+ \r
+ /* some modules have the tracker name empty */\r
+ if (!tracker[0])\r
+ strcpy(tracker,"Unknown tracker");\r
+\r
+#ifdef HAVE_SNPRINTF\r
+ snprintf(modtype,60,"%s (XM format %d.%02d)",\r
+ tracker,mh->version>>8,mh->version&0xff);\r
+#else\r
+ sprintf(modtype,"%s (XM format %d.%02d)",\r
+ tracker,mh->version>>8,mh->version&0xff);\r
+#endif\r
+ of.modtype = strdup(modtype);\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,1);\r
+ of.numpos = mh->songlength; /* copy the songlength */\r
+ of.reppos = mh->restart<mh->songlength?mh->restart:0;\r
+ of.numins = mh->numins;\r
+ of.flags |= UF_XMPERIODS | UF_INST | UF_NOWRAP | UF_FT2QUIRKS |\r
+ UF_PANNING;\r
+ if(mh->flags&1) of.flags |= UF_LINEAR;\r
+ of.bpmlimit = 32;\r
+\r
+ memset(of.chanvol,64,of.numchn); /* store channel volumes */\r
+\r
+ if(!AllocPositions(of.numpos+1)) return 0;\r
+ for(t=0;t<of.numpos;t++)\r
+ of.positions[t]=mh->orders[t];\r
+\r
+ /* We have to check for any pattern numbers in the order list greater than\r
+ the number of patterns total. If one or more is found, we set it equal to\r
+ the pattern total and make a dummy pattern to workaround the problem */\r
+ for(t=0;t<of.numpos;t++) {\r
+ if(of.positions[t]>=of.numpat) {\r
+ of.positions[t]=of.numpat;\r
+ dummypat=1;\r
+ }\r
+ }\r
+ if(dummypat) {\r
+ of.numpat++;of.numtrk+=of.numchn;\r
+ }\r
+\r
+ if(mh->version<0x0104) {\r
+ if(!LoadInstruments()) return 0;\r
+ if(!LoadPatterns(dummypat)) return 0;\r
+ for(t=0;t<of.numsmp;t++)\r
+ nextwav[t]+=_mm_ftell(modreader);\r
+ } else {\r
+ if(!LoadPatterns(dummypat)) return 0;\r
+ if(!LoadInstruments()) return 0;\r
+ }\r
+\r
+ if(!AllocSamples()) {\r
+ free(nextwav);free(wh);\r
+ nextwav=NULL;wh=NULL;\r
+ return 0;\r
+ }\r
+ q = of.samples;\r
+ s = wh;\r
+ for(u=0;u<of.numsmp;u++,q++,s++) {\r
+ q->samplename = DupStr(s->samplename,22,1);\r
+ q->length = s->length;\r
+ q->loopstart = s->loopstart;\r
+ q->loopend = s->loopstart+s->looplength;\r
+ q->volume = s->volume;\r
+ q->speed = s->finetune+128;\r
+ q->panning = s->panning;\r
+ q->seekpos = nextwav[u];\r
+ q->vibtype = s->vibtype;\r
+ q->vibsweep = s->vibsweep;\r
+ q->vibdepth = s->vibdepth;\r
+ q->vibrate = s->vibrate;\r
+\r
+ if(s->type & 0x10) {\r
+ q->length >>= 1;\r
+ q->loopstart >>= 1;\r
+ q->loopend >>= 1;\r
+ }\r
+\r
+ q->flags|=SF_OWNPAN|SF_DELTA|SF_SIGNED;\r
+ if(s->type&0x3) q->flags|=SF_LOOP;\r
+ if(s->type&0x2) q->flags|=SF_BIDI;\r
+ if(s->type&0x10) q->flags|=SF_16BITS;\r
+ }\r
+\r
+ d=of.instruments;\r
+ s=wh;\r
+ for(u=0;u<of.numins;u++,d++)\r
+ for(t=0;t<XMNOTECNT;t++) {\r
+ if (d->samplenumber[t]>=of.numsmp)\r
+ d->samplenote[t]=255;\r
+ else {\r
+ int note=t+s[d->samplenumber[t]].relnote;\r
+ d->samplenote[t]=(note<0)?0:note;\r
+ }\r
+ }\r
+\r
+ free(wh);free(nextwav);\r
+ wh=NULL;nextwav=NULL;\r
+ return 1;\r
+}\r
+\r
+CHAR *XM_LoadTitle(void)\r
+{\r
+ CHAR s[21];\r
+\r
+ _mm_fseek(modreader,17,SEEK_SET);\r
+ if(!_mm_read_UBYTES(s,21,modreader)) return NULL;\r
+\r
+ return(DupStr(s,21,1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_xm={\r
+ NULL,\r
+ "XM",\r
+ "XM (FastTracker 2)",\r
+ XM_Init,\r
+ XM_Test,\r
+ XM_Load,\r
+ XM_Cleanup,\r
+ XM_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r