Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_uni.c
diff --git a/apps/plugins/mikmod/loaders/load_uni.c b/apps/plugins/mikmod/loaders/load_uni.c
new file mode 100644 (file)
index 0000000..ae33671
--- /dev/null
@@ -0,0 +1,718 @@
+/*     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_uni.c,v 1.2 2004/02/06 19:29:03 raph Exp $\r
+\r
+  UNIMOD (libmikmod's and APlayer's internal module format) 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 UNIHEADER {\r
+       CHAR  id[4];\r
+       UBYTE numchn;\r
+       UWORD numpos;\r
+       UWORD reppos;\r
+       UWORD numpat;\r
+       UWORD numtrk;\r
+       UWORD numins;\r
+       UWORD numsmp;\r
+       UBYTE initspeed;\r
+       UBYTE inittempo;\r
+       UBYTE initvolume;\r
+       UWORD flags;\r
+       UBYTE numvoices;\r
+       UWORD bpmlimit;\r
+\r
+       UBYTE positions[256];\r
+       UBYTE panning[32];\r
+} UNIHEADER;\r
+\r
+typedef struct UNISMP05 {\r
+       UWORD c2spd;\r
+       UWORD transpose;\r
+       UBYTE volume;\r
+       UBYTE panning;\r
+       ULONG length;\r
+       ULONG loopstart;\r
+       ULONG loopend;\r
+       UWORD flags;\r
+       CHAR* samplename;\r
+       UBYTE vibtype;\r
+       UBYTE vibsweep;\r
+       UBYTE vibdepth;\r
+       UBYTE vibrate;\r
+} UNISMP05;\r
+\r
+/*========== Loader variables */\r
+\r
+static UWORD universion;\r
+static UNIHEADER mh;\r
+\r
+#define UNI_SMPINCR 64\r
+static UNISMP05 *wh=NULL,*s=NULL;\r
+\r
+/*========== Loader code */\r
+\r
+static char* readstring(void)\r
+{\r
+       char *s=NULL;\r
+       UWORD len;\r
+       \r
+       len=_mm_read_I_UWORD(modreader);\r
+       if(len) {\r
+               s=_mm_malloc(len+1);\r
+               _mm_read_UBYTES(s,len,modreader);\r
+               s[len]=0;\r
+       }\r
+       return s;\r
+}\r
+\r
+BOOL UNI_Test(void)\r
+{\r
+       char id[6];\r
+\r
+       if(!_mm_read_UBYTES(id,6,modreader)) return 0;\r
+\r
+       /* UNIMod created by MikCvt */\r
+       if(!(memcmp(id,"UN0",3))) {\r
+               if((id[3]>='4')&&(id[3]<='6')) return 1;\r
+       }\r
+       /* UNIMod created by APlayer */\r
+       if(!(memcmp(id,"APUN\01",5))) {\r
+               if((id[5]>=1)&&(id[5]<=6)) return 1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+BOOL UNI_Init(void)\r
+{\r
+       return 1;\r
+}\r
+\r
+void UNI_Cleanup(void)\r
+{\r
+       _mm_free(wh);\r
+       s=NULL;\r
+}\r
+\r
+static UBYTE* readtrack(void)\r
+{\r
+       UBYTE *t;\r
+       UWORD len;\r
+       int cur=0,chunk;\r
+\r
+       if(universion>=6)\r
+               len=_mm_read_M_UWORD(modreader);\r
+       else\r
+               len=_mm_read_I_UWORD(modreader);\r
+\r
+       if(!len) return NULL;\r
+       if(!(t=_mm_malloc(len))) return NULL;\r
+       _mm_read_UBYTES(t,len,modreader);\r
+\r
+       /* Check if the track is correct */\r
+       while(1) {\r
+               chunk=t[cur++];\r
+               if(!chunk) break;\r
+               chunk=(chunk&0x1f)-1;\r
+               while(chunk>0) {\r
+                       int opcode,oplen;\r
+\r
+                       if(cur>=len) {\r
+                               free(t);\r
+                               return NULL;\r
+                       }\r
+                       opcode=t[cur];\r
+\r
+                       /* Remap opcodes */\r
+                       if (universion <= 5) {\r
+                               if (opcode > 29) {\r
+                                       free(t);\r
+                                       return NULL;\r
+                               }\r
+                               switch (opcode) {\r
+                                       /* UNI_NOTE .. UNI_S3MEFFECTQ are the same */\r
+                                       case 25:\r
+                                               opcode = UNI_S3MEFFECTT;\r
+                                               break;\r
+                                       case 26:\r
+                                               opcode = UNI_XMEFFECTA;\r
+                                               break;\r
+                                       case 27:\r
+                                               opcode = UNI_XMEFFECTG;\r
+                                               break;\r
+                                       case 28:\r
+                                               opcode = UNI_XMEFFECTH;\r
+                                               break;\r
+                                       case 29:\r
+                                               opcode = UNI_XMEFFECTP;\r
+                                               break;\r
+                               }\r
+                       } else {\r
+                               /* APlayer < 1.05 does not have XMEFFECT6 */\r
+                               if (opcode >= UNI_XMEFFECT6 && universion < 0x105)\r
+                                       opcode++;\r
+                               /* APlayer < 1.03 does not have ITEFFECTT */\r
+                               if (opcode >= UNI_ITEFFECTT && universion < 0x103)\r
+                                       opcode++;\r
+                               /* APlayer < 1.02 does not have ITEFFECTZ */\r
+                               if (opcode >= UNI_ITEFFECTZ && universion < 0x102)\r
+                                       opcode++;\r
+                       }\r
+\r
+                       if((!opcode)||(opcode>=UNI_LAST)) {\r
+                               free(t);\r
+                               return NULL;\r
+                       }\r
+                       t[cur]=opcode;\r
+                       oplen=unioperands[opcode]+1;\r
+                       cur+=oplen;\r
+                       chunk-=oplen;\r
+               }\r
+               if((chunk<0)||(cur>=len)) {\r
+                       free(t);\r
+                       return NULL;\r
+               }\r
+       }\r
+       return t;\r
+}\r
+\r
+static BOOL loadsmp6(void)\r
+{\r
+       int t;\r
+       SAMPLE *s;\r
+\r
+       s=of.samples;\r
+       for(t=0;t<of.numsmp;t++,s++) {\r
+               int flags;\r
+\r
+               flags         = _mm_read_M_UWORD(modreader);\r
+               s->flags=0;\r
+               if(flags&0x0004) s->flags|=SF_STEREO;\r
+               if(flags&0x0002) s->flags|=SF_SIGNED;\r
+               if(flags&0x0001) s->flags|=SF_16BITS;\r
+               /* convert flags */\r
+               if(universion>=0x104) {\r
+                       if(flags&0x2000) s->flags|=SF_UST_LOOP;\r
+                       if(flags&0x1000) s->flags|=SF_OWNPAN;\r
+                       if(flags&0x0800) s->flags|=SF_SUSTAIN;\r
+                       if(flags&0x0400) s->flags|=SF_REVERSE;\r
+                       if(flags&0x0200) s->flags|=SF_BIDI;\r
+                       if(flags&0x0100) s->flags|=SF_LOOP;\r
+                       if(flags&0x0020) s->flags|=SF_ITPACKED;\r
+                       if(flags&0x0010) s->flags|=SF_DELTA;\r
+                       if(flags&0x0008) s->flags|=SF_BIG_ENDIAN;\r
+               } else if(universion>=0x102) {\r
+                       if(flags&0x0800) s->flags|=SF_UST_LOOP;\r
+                       if(flags&0x0400) s->flags|=SF_OWNPAN;\r
+                       if(flags&0x0200) s->flags|=SF_SUSTAIN;\r
+                       if(flags&0x0100) s->flags|=SF_REVERSE;\r
+                       if(flags&0x0080) s->flags|=SF_BIDI;\r
+                       if(flags&0x0040) s->flags|=SF_LOOP;\r
+                       if(flags&0x0020) s->flags|=SF_ITPACKED;\r
+                       if(flags&0x0010) s->flags|=SF_DELTA;\r
+                       if(flags&0x0008) s->flags|=SF_BIG_ENDIAN;\r
+               } else {\r
+                       if(flags&0x400) s->flags|=SF_UST_LOOP;\r
+                       if(flags&0x200) s->flags|=SF_OWNPAN;\r
+                       if(flags&0x100) s->flags|=SF_REVERSE;\r
+                       if(flags&0x080) s->flags|=SF_SUSTAIN;\r
+                       if(flags&0x040) s->flags|=SF_BIDI;\r
+                       if(flags&0x020) s->flags|=SF_LOOP;\r
+                       if(flags&0x010) s->flags|=SF_BIG_ENDIAN;\r
+                       if(flags&0x008) s->flags|=SF_DELTA;\r
+               }\r
+\r
+               s->speed      = _mm_read_M_ULONG(modreader);\r
+               s->volume     = _mm_read_UBYTE(modreader);\r
+               s->panning    = _mm_read_M_UWORD(modreader);\r
+               s->length     = _mm_read_M_ULONG(modreader);\r
+               s->loopstart  = _mm_read_M_ULONG(modreader);\r
+               s->loopend    = _mm_read_M_ULONG(modreader);\r
+               s->susbegin   = _mm_read_M_ULONG(modreader);\r
+               s->susend     = _mm_read_M_ULONG(modreader);\r
+               s->globvol    = _mm_read_UBYTE(modreader);\r
+               s->vibflags   = _mm_read_UBYTE(modreader);\r
+               s->vibtype    = _mm_read_UBYTE(modreader);\r
+               s->vibsweep   = _mm_read_UBYTE(modreader);\r
+               s->vibdepth   = _mm_read_UBYTE(modreader);\r
+               s->vibrate    = _mm_read_UBYTE(modreader);\r
+\r
+               s->samplename=readstring();\r
+\r
+               if(_mm_eof(modreader)) {\r
+                       _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+                       return 0;\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL loadinstr6(void)\r
+{\r
+       int t,w;\r
+       INSTRUMENT *i;\r
+\r
+       i=of.instruments;\r
+       for(t=0;t<of.numins;t++,i++) {\r
+               i->flags        = _mm_read_UBYTE(modreader);\r
+               i->nnatype      = _mm_read_UBYTE(modreader);\r
+               i->dca          = _mm_read_UBYTE(modreader);\r
+               i->dct          = _mm_read_UBYTE(modreader);\r
+               i->globvol      = _mm_read_UBYTE(modreader);\r
+               i->panning      = _mm_read_M_UWORD(modreader);\r
+               i->pitpansep    = _mm_read_UBYTE(modreader);\r
+               i->pitpancenter = _mm_read_UBYTE(modreader);\r
+               i->rvolvar      = _mm_read_UBYTE(modreader);\r
+               i->rpanvar      = _mm_read_UBYTE(modreader);\r
+               i->volfade      = _mm_read_M_UWORD(modreader);\r
+\r
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C\r
+#define UNI_LoadEnvelope6(name)                                                                                \\r
+               i-> name##flg=_mm_read_UBYTE(modreader);                                                \\r
+               i-> name##pts=_mm_read_UBYTE(modreader);                                                \\r
+               i-> name##susbeg=_mm_read_UBYTE(modreader);                                             \\r
+               i-> name##susend=_mm_read_UBYTE(modreader);                                             \\r
+               i-> name##beg=_mm_read_UBYTE(modreader);                                                \\r
+               i-> name##end=_mm_read_UBYTE(modreader);                                                \\r
+               for(w=0;w<(universion>=0x100?32:i-> name##pts);w++) {                   \\r
+                       i-> name##env[w].pos=_mm_read_M_SWORD(modreader);                       \\r
+                       i-> name##env[w].val=_mm_read_M_SWORD(modreader);                       \\r
+               }\r
+#else\r
+#define UNI_LoadEnvelope6(name)                                                                                \\r
+               i-> name/**/flg=_mm_read_UBYTE(modreader);                                              \\r
+               i-> name/**/pts=_mm_read_UBYTE(modreader);                                              \\r
+               i-> name/**/susbeg=_mm_read_UBYTE(modreader);                                   \\r
+               i-> name/**/susend=_mm_read_UBYTE(modreader);                                   \\r
+               i-> name/**/beg=_mm_read_UBYTE(modreader);                                              \\r
+               i-> name/**/end=_mm_read_UBYTE(modreader);                                              \\r
+               for (w=0;w<(universion>=0x100?32:i-> name/**/pts);w++) {                \\r
+                       i-> name/**/env[w].pos=_mm_read_M_SWORD(modreader);                     \\r
+                       i-> name/**/env[w].val=_mm_read_M_SWORD(modreader);                     \\r
+               }\r
+#endif\r
+\r
+               UNI_LoadEnvelope6(vol);\r
+               UNI_LoadEnvelope6(pan);\r
+               UNI_LoadEnvelope6(pit);\r
+#undef UNI_LoadEnvelope6\r
+\r
+               if(universion>=0x103)\r
+                       _mm_read_M_UWORDS(i->samplenumber,120,modreader);\r
+               else\r
+                       for(w=0;w<120;w++)\r
+                               i->samplenumber[w]=_mm_read_UBYTE(modreader);\r
+               _mm_read_UBYTES(i->samplenote,120,modreader);\r
+\r
+               i->insname=readstring();\r
+\r
+               if(_mm_eof(modreader)) {\r
+                       _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+                       return 0;\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL loadinstr5(void)\r
+{\r
+       INSTRUMENT *i;\r
+       int t;\r
+       UWORD wavcnt=0;\r
+       UBYTE vibtype,vibsweep,vibdepth,vibrate;\r
+\r
+       i=of.instruments;\r
+       for(of.numsmp=t=0;t<of.numins;t++,i++) {\r
+               int u,numsmp;\r
+\r
+               numsmp=_mm_read_UBYTE(modreader);\r
+\r
+               memset(i->samplenumber,0xff,INSTNOTES*sizeof(UWORD));\r
+               for(u=0;u<96;u++)\r
+                       i->samplenumber[u]=of.numsmp+_mm_read_UBYTE(modreader);\r
+\r
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C\r
+#define UNI_LoadEnvelope5(name)                                                                        \\r
+               i-> name##flg=_mm_read_UBYTE(modreader);                                        \\r
+               i-> name##pts=_mm_read_UBYTE(modreader);                                        \\r
+               i-> name##susbeg=_mm_read_UBYTE(modreader);                                     \\r
+               i-> name##susend=i-> name##susbeg;                                                      \\r
+               i-> name##beg=_mm_read_UBYTE(modreader);                                        \\r
+               i-> name##end=_mm_read_UBYTE(modreader);                                        \\r
+               for(u=0;u<12;u++) {                                                                                     \\r
+                       i-> name##env[u].pos=_mm_read_I_SWORD(modreader);               \\r
+                       i-> name##env[u].val=_mm_read_I_SWORD(modreader);               \\r
+               }\r
+#else\r
+#define UNI_LoadEnvelope5(name)                                                                        \\r
+               i-> name/**/flg=_mm_read_UBYTE(modreader);                                      \\r
+               i-> name/**/pts=_mm_read_UBYTE(modreader);                                      \\r
+               i-> name/**/susbeg=_mm_read_UBYTE(modreader);                           \\r
+               i-> name/**/susend=i-> name/**/susbeg;                                          \\r
+               i-> name/**/beg=_mm_read_UBYTE(modreader);                                      \\r
+               i-> name/**/end=_mm_read_UBYTE(modreader);                                      \\r
+               for(u=0;u<12;u++) {                                                                                     \\r
+                       i-> name/**/env[u].pos=_mm_read_I_SWORD(modreader);             \\r
+                       i-> name/**/env[u].val=_mm_read_I_SWORD(modreader);             \\r
+               }\r
+#endif\r
+\r
+               UNI_LoadEnvelope5(vol);\r
+               UNI_LoadEnvelope5(pan);\r
+#undef UNI_LoadEnvelope5\r
+\r
+               vibtype      =_mm_read_UBYTE(modreader);\r
+               vibsweep     =_mm_read_UBYTE(modreader);\r
+               vibdepth     =_mm_read_UBYTE(modreader);\r
+               vibrate      =_mm_read_UBYTE(modreader);\r
+\r
+               i->volfade=_mm_read_I_UWORD(modreader);\r
+               i->insname=readstring();\r
+\r
+               for(u=0;u<numsmp;u++,s++,of.numsmp++) {\r
+                       /* Allocate more room for sample information if necessary */\r
+                       if(of.numsmp+u==wavcnt) {\r
+                               wavcnt+=UNI_SMPINCR;\r
+                               if(!(wh=realloc(wh,wavcnt*sizeof(UNISMP05)))) {\r
+                                       _mm_errno=MMERR_OUT_OF_MEMORY;\r
+                                       return 0;\r
+                               }\r
+                               s=wh+(wavcnt-UNI_SMPINCR);\r
+                       }\r
+\r
+                       s->c2spd    =_mm_read_I_UWORD(modreader);\r
+                       s->transpose=_mm_read_SBYTE(modreader);\r
+                       s->volume   =_mm_read_UBYTE(modreader);\r
+                       s->panning  =_mm_read_UBYTE(modreader);\r
+                       s->length   =_mm_read_I_ULONG(modreader);\r
+                       s->loopstart=_mm_read_I_ULONG(modreader);\r
+                       s->loopend  =_mm_read_I_ULONG(modreader);\r
+                       s->flags    =_mm_read_I_UWORD(modreader);\r
+                       s->samplename=readstring();\r
+\r
+                       s->vibtype =vibtype;\r
+                       s->vibsweep=vibsweep;\r
+                       s->vibdepth=vibdepth;\r
+                       s->vibrate =vibrate;\r
+\r
+                       if(_mm_eof(modreader)) {\r
+                               free(wh);wh=NULL;\r
+                               _mm_errno=MMERR_LOADING_SAMPLEINFO;\r
+                               return 0;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /* sanity check */\r
+       if(!of.numsmp) {\r
+               if(wh) { free(wh);wh=NULL; }\r
+               _mm_errno=MMERR_LOADING_SAMPLEINFO;\r
+               return 0;\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL loadsmp5(void)\r
+{\r
+       int t,u;\r
+       SAMPLE *q;\r
+       INSTRUMENT *d;\r
+\r
+       q=of.samples;s=wh;\r
+       for(u=0;u<of.numsmp;u++,q++,s++) {\r
+               q->samplename=s->samplename;\r
+\r
+               q->length   =s->length;\r
+               q->loopstart=s->loopstart;\r
+               q->loopend  =s->loopend;\r
+               q->volume   =s->volume;\r
+               q->speed    =s->c2spd;\r
+               q->panning  =s->panning;\r
+               q->vibtype  =s->vibtype;\r
+               q->vibsweep =s->vibsweep;\r
+               q->vibdepth =s->vibdepth;\r
+               q->vibrate  =s->vibrate;\r
+\r
+               /* convert flags */\r
+               q->flags=0;\r
+               if(s->flags&128) q->flags|=SF_REVERSE;\r
+               if(s->flags& 64) q->flags|=SF_SUSTAIN;\r
+               if(s->flags& 32) q->flags|=SF_BIDI;\r
+               if(s->flags& 16) q->flags|=SF_LOOP;\r
+               if(s->flags&  8) q->flags|=SF_BIG_ENDIAN;\r
+               if(s->flags&  4) q->flags|=SF_DELTA;\r
+               if(s->flags&  2) q->flags|=SF_SIGNED;\r
+               if(s->flags&  1) q->flags|=SF_16BITS;\r
+       }\r
+\r
+       d=of.instruments;s=wh;\r
+       for(u=0;u<of.numins;u++,d++)\r
+               for(t=0;t<INSTNOTES;t++)\r
+                       d->samplenote[t]=(d->samplenumber[t]>=of.numsmp)?\r
+                         255:(t+s[d->samplenumber[t]].transpose);\r
+\r
+       free(wh);wh=NULL;\r
+\r
+       return 1;\r
+}\r
+\r
+BOOL UNI_Load(BOOL curious)\r
+{\r
+       int t;\r
+       char *modtype,*oldtype=NULL;\r
+       INSTRUMENT *d;\r
+       SAMPLE *q;\r
+       (void)curious;\r
+       \r
+       /* read module header */\r
+       _mm_read_UBYTES(mh.id,4,modreader);\r
+       if(mh.id[3]!='N')\r
+               universion=mh.id[3]-'0';\r
+       else\r
+               universion=0x100;\r
+\r
+       if(universion>=6) {\r
+               if (universion==6)\r
+                       _mm_read_UBYTE(modreader);\r
+               else\r
+                       universion=_mm_read_M_UWORD(modreader);\r
+\r
+               mh.flags     =_mm_read_M_UWORD(modreader);\r
+               mh.numchn    =_mm_read_UBYTE(modreader);\r
+               mh.numvoices =_mm_read_UBYTE(modreader);\r
+               mh.numpos    =_mm_read_M_UWORD(modreader);\r
+               mh.numpat    =_mm_read_M_UWORD(modreader);\r
+               mh.numtrk    =_mm_read_M_UWORD(modreader);\r
+               mh.numins    =_mm_read_M_UWORD(modreader);\r
+               mh.numsmp    =_mm_read_M_UWORD(modreader);\r
+               mh.reppos    =_mm_read_M_UWORD(modreader);\r
+               mh.initspeed =_mm_read_UBYTE(modreader);\r
+               mh.inittempo =_mm_read_UBYTE(modreader);\r
+               mh.initvolume=_mm_read_UBYTE(modreader);\r
+               /* I expect this to show up soon in APlayer 1.06 format */\r
+               if (universion >= 0x106)\r
+                       mh.bpmlimit=_mm_read_M_UWORD(modreader);\r
+               else\r
+                       mh.bpmlimit=32;\r
+\r
+               mh.flags &= UF_XMPERIODS | UF_LINEAR | UF_INST | UF_NNA;\r
+               mh.flags |= UF_PANNING;\r
+       } else {\r
+               mh.numchn    =_mm_read_UBYTE(modreader);\r
+               mh.numpos    =_mm_read_I_UWORD(modreader);\r
+               mh.reppos    =(universion==5)?_mm_read_I_UWORD(modreader):0;\r
+               mh.numpat    =_mm_read_I_UWORD(modreader);\r
+               mh.numtrk    =_mm_read_I_UWORD(modreader);\r
+               mh.numins    =_mm_read_I_UWORD(modreader);\r
+               mh.initspeed =_mm_read_UBYTE(modreader);\r
+               mh.inittempo =_mm_read_UBYTE(modreader);\r
+               _mm_read_UBYTES(mh.positions,256,modreader);\r
+               _mm_read_UBYTES(mh.panning,32,modreader);\r
+               mh.flags     =_mm_read_UBYTE(modreader);\r
+               mh.bpmlimit  =32;\r
+\r
+               mh.flags &= UF_XMPERIODS | UF_LINEAR;\r
+               mh.flags |= UF_INST | UF_NOWRAP | UF_PANNING;\r
+       }\r
+       \r
+       /* set module parameters */\r
+       of.flags     =mh.flags;\r
+       of.numchn    =mh.numchn;\r
+       of.numpos    =mh.numpos;\r
+       of.numpat    =mh.numpat;\r
+       of.numtrk    =mh.numtrk;\r
+       of.numins    =mh.numins;\r
+       of.reppos    =mh.reppos;\r
+       of.initspeed =mh.initspeed;\r
+       of.inittempo =mh.inittempo;\r
+       if(mh.bpmlimit)\r
+               of.bpmlimit=mh.bpmlimit;\r
+       else\r
+               /* be bug-compatible with older releases */\r
+               of.bpmlimit=32;\r
+\r
+       of.songname=readstring();\r
+       if(universion<0x102)\r
+               oldtype=readstring();\r
+       if(oldtype) {\r
+               int len=strlen(oldtype)+20;\r
+               if(!(modtype=_mm_malloc(len))) return 0;\r
+#ifdef HAVE_SNPRINTF\r
+               snprintf(modtype,len,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype);\r
+#else\r
+               sprintf(modtype,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype);\r
+#endif\r
+       } else {\r
+               if(!(modtype=_mm_malloc(10))) return 0;\r
+#ifdef HAVE_SNPRINTF\r
+               snprintf(modtype,10,"%s",(universion>=0x100)?"APlayer":"MikCvt3");\r
+#else\r
+               sprintf(modtype,"%s",(universion>=0x100)?"APlayer":"MikCvt3");\r
+#endif\r
+       }\r
+       of.modtype=strdup(modtype);\r
+       free(modtype);free(oldtype);\r
+       of.comment=readstring();\r
+\r
+       if(universion>=6) {\r
+               of.numvoices=mh.numvoices;\r
+               of.initvolume=mh.initvolume;\r
+       }\r
+\r
+       if(_mm_eof(modreader)) {\r
+               _mm_errno=MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* positions */\r
+       if(!AllocPositions(of.numpos)) return 0;\r
+       if(universion>=6) {\r
+               if(universion>=0x100)\r
+                       _mm_read_M_UWORDS(of.positions,of.numpos,modreader);\r
+               else\r
+                       for(t=0;t<of.numpos;t++) of.positions[t]=_mm_read_UBYTE(modreader);\r
+               _mm_read_M_UWORDS(of.panning,of.numchn,modreader);\r
+               _mm_read_UBYTES(of.chanvol,of.numchn,modreader);\r
+       } else {\r
+               if((mh.numpos>256)||(mh.numchn>32)) {\r
+                       _mm_errno=MMERR_LOADING_HEADER;\r
+                       return 0;\r
+               }\r
+               for(t=0;t<of.numpos;t++) of.positions[t]=mh.positions[t];\r
+               for(t=0;t<of.numchn;t++) of.panning[t]=mh.panning[t];\r
+       }\r
+       /* convert the ``end of song'' pattern code if necessary */\r
+       if(universion<0x106)\r
+               for(t=0;t<of.numpos;t++)\r
+                       if(of.positions[t]==255) of.positions[t]=LAST_PATTERN;\r
+\r
+       /* instruments and samples */\r
+       if(universion>=6) {\r
+               of.numsmp=mh.numsmp;\r
+               if(!AllocSamples()) return 0;\r
+               if(!loadsmp6()) return 0;\r
+\r
+               if(of.flags&UF_INST) {\r
+                       if(!AllocInstruments()) return 0;\r
+                       if(!loadinstr6()) return 0;\r
+               }\r
+       } else {\r
+               if(!AllocInstruments()) return 0;\r
+               if(!loadinstr5()) return 0;\r
+               if(!AllocSamples()) {\r
+                       if(wh) { free(wh);wh=NULL; }\r
+                       return 0;\r
+               }\r
+               if(!loadsmp5()) return 0;\r
+\r
+               /* check if the original file had no instruments */\r
+               if(of.numsmp==of.numins) {\r
+                       for(t=0,d=of.instruments;t<of.numins;t++,d++) {\r
+                               int u;\r
+\r
+                               if((d->volpts)||(d->panpts)||(d->globvol!=64)) break;\r
+                               for(u=0;u<96;u++)\r
+                                       if((d->samplenumber[u]!=t)||(d->samplenote[u]!=u)) break;\r
+                               if(u!=96) break;\r
+                       }\r
+                       if(t==of.numins) {\r
+                               of.flags&=~UF_INST;\r
+                               of.flags&=~UF_NOWRAP;\r
+                               for(t=0,d=of.instruments,q=of.samples;t<of.numins;t++,d++,q++) {\r
+                                       q->samplename=d->insname;\r
+                                       d->insname=NULL;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /* patterns */\r
+       if(!AllocPatterns()) return 0;\r
+       if(universion>=6) {\r
+               _mm_read_M_UWORDS(of.pattrows,of.numpat,modreader);\r
+               _mm_read_M_UWORDS(of.patterns,of.numpat*of.numchn,modreader);\r
+       } else {\r
+               _mm_read_I_UWORDS(of.pattrows,of.numpat,modreader);\r
+               _mm_read_I_UWORDS(of.patterns,of.numpat*of.numchn,modreader);\r
+       }\r
+\r
+       /* tracks */\r
+       if(!AllocTracks()) return 0;\r
+       for(t=0;t<of.numtrk;t++)\r
+               if(!(of.tracks[t]=readtrack())) {\r
+                       _mm_errno=MMERR_LOADING_TRACK;\r
+                       return 0;\r
+               }\r
+\r
+       return 1;\r
+}\r
+\r
+CHAR *UNI_LoadTitle(void)\r
+{\r
+       UBYTE ver;\r
+       int posit[3]={304,306,26};\r
+\r
+       _mm_fseek(modreader,3,SEEK_SET);\r
+       ver=_mm_read_UBYTE(modreader);\r
+       if(ver=='N') ver='6';\r
+\r
+       _mm_fseek(modreader,posit[ver-'4'],SEEK_SET);\r
+       return readstring();\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_uni={\r
+       NULL,\r
+       "UNI",\r
+       "APUN (APlayer) and UNI (MikMod)",\r
+       UNI_Init,\r
+       UNI_Test,\r
+       UNI_Load,\r
+       UNI_Cleanup,\r
+       UNI_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r