Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_xm.c
diff --git a/apps/plugins/mikmod/loaders/load_xm.c b/apps/plugins/mikmod/loaders/load_xm.c
new file mode 100644 (file)
index 0000000..fcf958d
--- /dev/null
@@ -0,0 +1,816 @@
+/*     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