Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_med.c
diff --git a/apps/plugins/mikmod/loaders/load_med.c b/apps/plugins/mikmod/loaders/load_med.c
new file mode 100644 (file)
index 0000000..3078dd2
--- /dev/null
@@ -0,0 +1,719 @@
+/*     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_med.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  Amiga MED 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 information */\r
+\r
+typedef struct MEDHEADER {\r
+       ULONG id;\r
+       ULONG modlen;\r
+       ULONG MEDSONGP;                         /* struct MEDSONG *song; */\r
+       UWORD psecnum;                          /* for the player routine, MMD2 only */\r
+       UWORD pseq;                                     /*  "   "   "   " */\r
+       ULONG MEDBlockPP;                       /* struct MEDBlock **blockarr; */\r
+       ULONG reserved1;\r
+       ULONG MEDINSTHEADERPP;          /* struct MEDINSTHEADER **smplarr; */\r
+       ULONG reserved2;\r
+       ULONG MEDEXPP;                          /* struct MEDEXP *expdata; */\r
+       ULONG reserved3;\r
+       UWORD pstate;                           /* some data for the player routine */\r
+       UWORD pblock;\r
+       UWORD pline;\r
+       UWORD pseqnum;\r
+       SWORD actplayline;\r
+       UBYTE counter;\r
+       UBYTE extra_songs;                      /* number of songs - 1 */\r
+} MEDHEADER;\r
+\r
+typedef struct MEDSAMPLE {\r
+       UWORD rep, replen;                      /* offs: 0(s), 2(s) */\r
+       UBYTE midich;                           /* offs: 4(s) */\r
+       UBYTE midipreset;                       /* offs: 5(s) */\r
+       UBYTE svol;                                     /* offs: 6(s) */\r
+       SBYTE strans;                           /* offs: 7(s) */\r
+} MEDSAMPLE;\r
+\r
+typedef struct MEDSONG {\r
+       MEDSAMPLE sample[63];           /* 63 * 8 bytes = 504 bytes */\r
+       UWORD numblocks;                        /* offs: 504 */\r
+       UWORD songlen;                          /* offs: 506 */\r
+       UBYTE playseq[256];                     /* offs: 508 */\r
+       UWORD deftempo;                         /* offs: 764 */\r
+       SBYTE playtransp;                       /* offs: 766 */\r
+       UBYTE flags;                            /* offs: 767 */\r
+       UBYTE flags2;                           /* offs: 768 */\r
+       UBYTE tempo2;                           /* offs: 769 */\r
+       UBYTE trkvol[16];                       /* offs: 770 */\r
+       UBYTE mastervol;                        /* offs: 786 */\r
+       UBYTE numsamples;                       /* offs: 787 */\r
+} MEDSONG;\r
+\r
+typedef struct MEDEXP {\r
+       ULONG nextmod;                          /* pointer to next module */\r
+       ULONG exp_smp;                          /* pointer to MEDINSTEXT array */\r
+       UWORD s_ext_entries;\r
+       UWORD s_ext_entrsz;\r
+       ULONG annotxt;                          /* pointer to annotation text */\r
+       ULONG annolen;\r
+       ULONG iinfo;                            /* pointer to MEDINSTINFO array */\r
+       UWORD i_ext_entries;\r
+       UWORD i_ext_entrsz;\r
+       ULONG jumpmask;\r
+       ULONG rgbtable;\r
+       ULONG channelsplit;\r
+       ULONG n_info;\r
+       ULONG songname;                         /* pointer to songname */\r
+       ULONG songnamelen;\r
+       ULONG dumps;\r
+       ULONG reserved2[7];\r
+} MEDEXP;\r
+\r
+typedef struct MMD0NOTE {\r
+       UBYTE a, b, c;\r
+} MMD0NOTE;\r
+\r
+typedef struct MMD1NOTE {\r
+       UBYTE a, b, c, d;\r
+} MMD1NOTE;\r
+\r
+typedef struct MEDINSTHEADER {\r
+       ULONG length;\r
+       SWORD type;\r
+       /* Followed by actual data */\r
+} MEDINSTHEADER;\r
+\r
+typedef struct MEDINSTEXT {\r
+       UBYTE hold;\r
+       UBYTE decay;\r
+       UBYTE suppress_midi_off;\r
+       SBYTE finetune;\r
+} MEDINSTEXT;\r
+\r
+typedef struct MEDINSTINFO {\r
+       UBYTE name[40];\r
+} MEDINSTINFO;\r
+\r
+/*========== Loader variables */\r
+\r
+#define MMD0_string 0x4D4D4430\r
+#define MMD1_string 0x4D4D4431\r
+\r
+static MEDHEADER *mh = NULL;\r
+static MEDSONG *ms = NULL;\r
+static MEDEXP *me = NULL;\r
+static ULONG *ba = NULL;\r
+static MMD0NOTE *mmd0pat = NULL;\r
+static MMD1NOTE *mmd1pat = NULL;\r
+\r
+static BOOL decimalvolumes;\r
+static BOOL bpmtempos;\r
+\r
+#define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)]\r
+#define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)]\r
+\r
+static CHAR MED_Version[] = "OctaMED (MMDx)";\r
+\r
+/*========== Loader code */\r
+\r
+BOOL MED_Test(void)\r
+{\r
+       UBYTE id[4];\r
+\r
+       if (!_mm_read_UBYTES(id, 4, modreader))\r
+               return 0;\r
+       if ((!memcmp(id, "MMD0", 4)) || (!memcmp(id, "MMD1", 4)))\r
+               return 1;\r
+       return 0;\r
+}\r
+\r
+BOOL MED_Init(void)\r
+{\r
+       if (!(me = (MEDEXP *)_mm_malloc(sizeof(MEDEXP))))\r
+               return 0;\r
+       if (!(mh = (MEDHEADER *)_mm_malloc(sizeof(MEDHEADER))))\r
+               return 0;\r
+       if (!(ms = (MEDSONG *)_mm_malloc(sizeof(MEDSONG))))\r
+               return 0;\r
+       return 1;\r
+}\r
+\r
+void MED_Cleanup(void)\r
+{\r
+       _mm_free(me);\r
+       _mm_free(mh);\r
+       _mm_free(ms);\r
+       _mm_free(ba);\r
+       _mm_free(mmd0pat);\r
+       _mm_free(mmd1pat);\r
+}\r
+\r
+static void EffectCvt(UBYTE eff, UBYTE dat)\r
+{\r
+       switch (eff) {\r
+               /* 0x0 0x1 0x2 0x3 0x4 PT effects */\r
+         case 0x5:                             /* PT vibrato with speed/depth nibbles swapped */\r
+               UniPTEffect(0x4, (dat >> 4) | ((dat & 0xf) << 4));\r
+               break;\r
+               /* 0x6 0x7 not used */\r
+         case 0x6:\r
+         case 0x7:\r
+               break;\r
+         case 0x8:                             /* midi hold/decay */\r
+               break;\r
+         case 0x9:\r
+               if (bpmtempos) {\r
+                       if (!dat)\r
+                               dat = of.initspeed;\r
+                       UniEffect(UNI_S3MEFFECTA, dat);\r
+               } else {\r
+                       if (dat <= 0x20) {\r
+                               if (!dat)\r
+                                       dat = of.initspeed;\r
+                               else\r
+                                       dat /= 4;\r
+                               UniPTEffect(0xf, dat);\r
+                       } else\r
+                               UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / (33 * 4));\r
+               }\r
+               break;\r
+               /* 0xa 0xb PT effects */\r
+         case 0xc:\r
+               if (decimalvolumes)\r
+                       dat = (dat >> 4) * 10 + (dat & 0xf);\r
+               UniPTEffect(0xc, dat);\r
+               break;\r
+         case 0xd:                             /* same as PT volslide */\r
+               UniPTEffect(0xa, dat);\r
+               break;\r
+         case 0xe:                             /* synth jmp - midi */\r
+               break;\r
+         case 0xf:\r
+               switch (dat) {\r
+                 case 0:                               /* patternbreak */\r
+                       UniPTEffect(0xd, 0);\r
+                       break;\r
+                 case 0xf1:                    /* play note twice */\r
+                       UniWriteByte(UNI_MEDEFFECTF1);\r
+                       break;\r
+                 case 0xf2:                    /* delay note */\r
+                       UniWriteByte(UNI_MEDEFFECTF2);\r
+                       break;\r
+                 case 0xf3:                    /* play note three times */\r
+                       UniWriteByte(UNI_MEDEFFECTF3);\r
+                       break;\r
+                 case 0xfe:                    /* stop playing */\r
+                       UniPTEffect(0xb, of.numpat);\r
+                       break;\r
+                 case 0xff:                    /* note cut */\r
+                       UniPTEffect(0xc, 0);\r
+                       break;\r
+                 default:\r
+                       if (dat <= 10)\r
+                               UniPTEffect(0xf, dat);\r
+                       else if (dat <= 240) {\r
+                               if (bpmtempos)\r
+                                       UniPTEffect(0xf, (dat < 32) ? 32 : dat);\r
+                               else\r
+                                       UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / 33);\r
+                       }\r
+               }\r
+               break;\r
+         default:                                      /* all normal PT effects are handled here */\r
+               UniPTEffect(eff, dat);\r
+               break;\r
+       }\r
+}\r
+\r
+static UBYTE *MED_Convert1(int count, int col)\r
+{\r
+       int t;\r
+       UBYTE inst, note, eff, dat;\r
+       MMD1NOTE *n;\r
+\r
+       UniReset();\r
+       for (t = 0; t < count; t++) {\r
+               n = &d1note(t, col);\r
+\r
+               note = n->a & 0x7f;\r
+               inst = n->b & 0x3f;\r
+               eff = n->c & 0xf;\r
+               dat = n->d;\r
+\r
+               if (inst)\r
+                       UniInstrument(inst - 1);\r
+               if (note)\r
+                       UniNote(note + 3 * OCTAVE - 1);\r
+               EffectCvt(eff, dat);\r
+               UniNewline();\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+static UBYTE *MED_Convert0(int count, int col)\r
+{\r
+       int t;\r
+       UBYTE a, b, inst, note, eff, dat;\r
+       MMD0NOTE *n;\r
+\r
+       UniReset();\r
+       for (t = 0; t < count; t++) {\r
+               n = &d0note(t, col);\r
+               a = n->a;\r
+               b = n->b;\r
+\r
+               note = a & 0x3f;\r
+               a >>= 6;\r
+               a = ((a & 1) << 1) | (a >> 1);\r
+               inst = (b >> 4) | (a << 4);\r
+               eff = b & 0xf;\r
+               dat = n->c;\r
+\r
+               if (inst)\r
+                       UniInstrument(inst - 1);\r
+               if (note)\r
+                       UniNote(note + 3 * OCTAVE - 1);\r
+               EffectCvt(eff, dat);\r
+               UniNewline();\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+static BOOL LoadMEDPatterns(void)\r
+{\r
+       int t, row, col;\r
+       UWORD numtracks, numlines, maxlines = 0, track = 0;\r
+       MMD0NOTE *mmdp;\r
+\r
+       /* first, scan patterns to see how many channels are used */\r
+       for (t = 0; t < of.numpat; t++) {\r
+               _mm_fseek(modreader, ba[t], SEEK_SET);\r
+               numtracks = _mm_read_UBYTE(modreader);\r
+               numlines = _mm_read_UBYTE(modreader);\r
+\r
+               if (numtracks > of.numchn)\r
+                       of.numchn = numtracks;\r
+               if (numlines > maxlines)\r
+                       maxlines = numlines;\r
+       }\r
+\r
+       of.numtrk = of.numpat * of.numchn;\r
+       if (!AllocTracks())\r
+               return 0;\r
+       if (!AllocPatterns())\r
+               return 0;\r
+\r
+       if (!\r
+               (mmd0pat =\r
+                (MMD0NOTE *)_mm_calloc(of.numchn * (maxlines + 1),\r
+                                                               sizeof(MMD0NOTE)))) return 0;\r
+\r
+       /* second read: read and convert patterns */\r
+       for (t = 0; t < of.numpat; t++) {\r
+               _mm_fseek(modreader, ba[t], SEEK_SET);\r
+               numtracks = _mm_read_UBYTE(modreader);\r
+               numlines = _mm_read_UBYTE(modreader);\r
+\r
+               of.pattrows[t] = ++numlines;\r
+               memset(mmdp = mmd0pat, 0, of.numchn * maxlines * sizeof(MMD0NOTE));\r
+               for (row = numlines; row; row--) {\r
+                       for (col = numtracks; col; col--, mmdp++) {\r
+                               mmdp->a = _mm_read_UBYTE(modreader);\r
+                               mmdp->b = _mm_read_UBYTE(modreader);\r
+                               mmdp->c = _mm_read_UBYTE(modreader);\r
+                       }\r
+               }\r
+\r
+               for (col = 0; col < of.numchn; col++)\r
+                       of.tracks[track++] = MED_Convert0(numlines, col);\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL LoadMMD1Patterns(void)\r
+{\r
+       int t, row, col;\r
+       UWORD numtracks, numlines, maxlines = 0, track = 0;\r
+       MMD1NOTE *mmdp;\r
+\r
+       /* first, scan patterns to see how many channels are used */\r
+       for (t = 0; t < of.numpat; t++) {\r
+               _mm_fseek(modreader, ba[t], SEEK_SET);\r
+               numtracks = _mm_read_M_UWORD(modreader);\r
+               numlines = _mm_read_M_UWORD(modreader);\r
+               if (numtracks > of.numchn)\r
+                       of.numchn = numtracks;\r
+               if (numlines > maxlines)\r
+                       maxlines = numlines;\r
+       }\r
+\r
+       of.numtrk = of.numpat * of.numchn;\r
+       if (!AllocTracks())\r
+               return 0;\r
+       if (!AllocPatterns())\r
+               return 0;\r
+\r
+       if (!\r
+               (mmd1pat =\r
+                (MMD1NOTE *)_mm_calloc(of.numchn * (maxlines + 1),\r
+                                                               sizeof(MMD1NOTE)))) return 0;\r
+\r
+       /* second read: really read and convert patterns */\r
+       for (t = 0; t < of.numpat; t++) {\r
+               _mm_fseek(modreader, ba[t], SEEK_SET);\r
+               numtracks = _mm_read_M_UWORD(modreader);\r
+               numlines = _mm_read_M_UWORD(modreader);\r
+\r
+               _mm_fseek(modreader, sizeof(ULONG), SEEK_CUR);\r
+               of.pattrows[t] = ++numlines;\r
+               memset(mmdp = mmd1pat, 0, of.numchn * maxlines * sizeof(MMD1NOTE));\r
+\r
+               for (row = numlines; row; row--) {\r
+                       for (col = numtracks; col; col--, mmdp++) {\r
+                               mmdp->a = _mm_read_UBYTE(modreader);\r
+                               mmdp->b = _mm_read_UBYTE(modreader);\r
+                               mmdp->c = _mm_read_UBYTE(modreader);\r
+                               mmdp->d = _mm_read_UBYTE(modreader);\r
+                       }\r
+               }\r
+\r
+               for (col = 0; col < of.numchn; col++)\r
+                       of.tracks[track++] = MED_Convert1(numlines, col);\r
+       }\r
+       return 1;\r
+}\r
+\r
+BOOL MED_Load(BOOL curious)\r
+{\r
+       int t;\r
+       ULONG sa[64];\r
+       MEDINSTHEADER s;\r
+       SAMPLE *q;\r
+       MEDSAMPLE *mss;\r
+\r
+       /* try to read module header */\r
+       mh->id = _mm_read_M_ULONG(modreader);\r
+       mh->modlen = _mm_read_M_ULONG(modreader);\r
+       mh->MEDSONGP = _mm_read_M_ULONG(modreader);\r
+       mh->psecnum = _mm_read_M_UWORD(modreader);\r
+       mh->pseq = _mm_read_M_UWORD(modreader);\r
+       mh->MEDBlockPP = _mm_read_M_ULONG(modreader);\r
+       mh->reserved1 = _mm_read_M_ULONG(modreader);\r
+       mh->MEDINSTHEADERPP = _mm_read_M_ULONG(modreader);\r
+       mh->reserved2 = _mm_read_M_ULONG(modreader);\r
+       mh->MEDEXPP = _mm_read_M_ULONG(modreader);\r
+       mh->reserved3 = _mm_read_M_ULONG(modreader);\r
+       mh->pstate = _mm_read_M_UWORD(modreader);\r
+       mh->pblock = _mm_read_M_UWORD(modreader);\r
+       mh->pline = _mm_read_M_UWORD(modreader);\r
+       mh->pseqnum = _mm_read_M_UWORD(modreader);\r
+       mh->actplayline = _mm_read_M_SWORD(modreader);\r
+       mh->counter = _mm_read_UBYTE(modreader);\r
+       mh->extra_songs = _mm_read_UBYTE(modreader);\r
+\r
+       /* Seek to MEDSONG struct */\r
+       _mm_fseek(modreader, mh->MEDSONGP, SEEK_SET);\r
+\r
+       /* Load the MED Song Header */\r
+       mss = ms->sample;                       /* load the sample data first */\r
+       for (t = 63; t; t--, mss++) {\r
+               mss->rep = _mm_read_M_UWORD(modreader);\r
+               mss->replen = _mm_read_M_UWORD(modreader);\r
+               mss->midich = _mm_read_UBYTE(modreader);\r
+               mss->midipreset = _mm_read_UBYTE(modreader);\r
+               mss->svol = _mm_read_UBYTE(modreader);\r
+               mss->strans = _mm_read_SBYTE(modreader);\r
+       }\r
+\r
+       ms->numblocks = _mm_read_M_UWORD(modreader);\r
+       ms->songlen = _mm_read_M_UWORD(modreader);\r
+       _mm_read_UBYTES(ms->playseq, 256, modreader);\r
+       ms->deftempo = _mm_read_M_UWORD(modreader);\r
+       ms->playtransp = _mm_read_SBYTE(modreader);\r
+       ms->flags = _mm_read_UBYTE(modreader);\r
+       ms->flags2 = _mm_read_UBYTE(modreader);\r
+       ms->tempo2 = _mm_read_UBYTE(modreader);\r
+       _mm_read_UBYTES(ms->trkvol, 16, modreader);\r
+       ms->mastervol = _mm_read_UBYTE(modreader);\r
+       ms->numsamples = _mm_read_UBYTE(modreader);\r
+\r
+       /* check for a bad header */\r
+       if (_mm_eof(modreader)) {\r
+               _mm_errno = MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* load extension structure */\r
+       if (mh->MEDEXPP) {\r
+               _mm_fseek(modreader, mh->MEDEXPP, SEEK_SET);\r
+               me->nextmod = _mm_read_M_ULONG(modreader);\r
+               me->exp_smp = _mm_read_M_ULONG(modreader);\r
+               me->s_ext_entries = _mm_read_M_UWORD(modreader);\r
+               me->s_ext_entrsz = _mm_read_M_UWORD(modreader);\r
+               me->annotxt = _mm_read_M_ULONG(modreader);\r
+               me->annolen = _mm_read_M_ULONG(modreader);\r
+               me->iinfo = _mm_read_M_ULONG(modreader);\r
+               me->i_ext_entries = _mm_read_M_UWORD(modreader);\r
+               me->i_ext_entrsz = _mm_read_M_UWORD(modreader);\r
+               me->jumpmask = _mm_read_M_ULONG(modreader);\r
+               me->rgbtable = _mm_read_M_ULONG(modreader);\r
+               me->channelsplit = _mm_read_M_ULONG(modreader);\r
+               me->n_info = _mm_read_M_ULONG(modreader);\r
+               me->songname = _mm_read_M_ULONG(modreader);\r
+               me->songnamelen = _mm_read_M_ULONG(modreader);\r
+               me->dumps = _mm_read_M_ULONG(modreader);\r
+       }\r
+\r
+       /* seek to and read the samplepointer array */\r
+       _mm_fseek(modreader, mh->MEDINSTHEADERPP, SEEK_SET);\r
+       if (!_mm_read_M_ULONGS(sa, ms->numsamples, modreader)) {\r
+               _mm_errno = MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* alloc and read the blockpointer array */\r
+       if (!(ba = (ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG))))\r
+               return 0;\r
+       _mm_fseek(modreader, mh->MEDBlockPP, SEEK_SET);\r
+       if (!_mm_read_M_ULONGS(ba, ms->numblocks, modreader)) {\r
+               _mm_errno = MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* copy song positions */\r
+       if (!AllocPositions(ms->songlen))\r
+               return 0;\r
+       for (t = 0; t < ms->songlen; t++)\r
+               of.positions[t] = ms->playseq[t];\r
+\r
+       decimalvolumes = (ms->flags & 0x10) ? 0 : 1;\r
+       bpmtempos = (ms->flags2 & 0x20) ? 1 : 0;\r
+\r
+       if (bpmtempos) {\r
+               int bpmlen = (ms->flags2 & 0x1f) + 1;\r
+               of.initspeed = ms->tempo2;\r
+               of.inittempo = ms->deftempo * bpmlen / 4;\r
+\r
+               if (bpmlen != 4) {\r
+                       /* Let's do some math : compute GCD of BPM beat length and speed */\r
+                       int a, b;\r
+\r
+                       a = bpmlen;\r
+                       b = ms->tempo2;\r
+\r
+                       if (a > b) {\r
+                               t = b;\r
+                               b = a;\r
+                               a = t;\r
+                       }\r
+                       while ((a != b) && (a)) {\r
+                               t = a;\r
+                               a = b - a;\r
+                               b = t;\r
+                               if (a > b) {\r
+                                       t = b;\r
+                                       b = a;\r
+                                       a = t;\r
+                               }\r
+                       }\r
+\r
+                       of.initspeed /= b;\r
+                       of.inittempo = ms->deftempo * bpmlen / (4 * b);\r
+               }\r
+       } else {\r
+               of.initspeed = ms->tempo2;\r
+               of.inittempo = ms->deftempo ? ((UWORD)ms->deftempo * 125) / 33 : 128;\r
+               if ((ms->deftempo <= 10) && (ms->deftempo))\r
+                       of.inittempo = (of.inittempo * 33) / 6;\r
+               of.flags |= UF_HIGHBPM;\r
+       }\r
+       MED_Version[12] = mh->id;\r
+       of.modtype = strdup(MED_Version);\r
+       of.numchn = 0;                          /* will be counted later */\r
+       of.numpat = ms->numblocks;\r
+       of.numpos = ms->songlen;\r
+       of.numins = ms->numsamples;\r
+       of.numsmp = of.numins;\r
+       of.reppos = 0;\r
+       if ((mh->MEDEXPP) && (me->songname) && (me->songnamelen)) {\r
+               char *name;\r
+\r
+               _mm_fseek(modreader, me->songname, SEEK_SET);\r
+               name = _mm_malloc(me->songnamelen);\r
+               _mm_read_UBYTES(name, me->songnamelen, modreader);\r
+               of.songname = DupStr(name, me->songnamelen, 1);\r
+               free(name);\r
+       } else\r
+               of.songname = DupStr(NULL, 0, 0);\r
+       if ((mh->MEDEXPP) && (me->annotxt) && (me->annolen)) {\r
+               _mm_fseek(modreader, me->annotxt, SEEK_SET);\r
+               ReadComment(me->annolen);\r
+       }\r
+\r
+       if (!AllocSamples())\r
+               return 0;\r
+       q = of.samples;\r
+       for (t = 0; t < of.numins; t++) {\r
+               q->flags = SF_SIGNED;\r
+               q->volume = 64;\r
+               if (sa[t]) {\r
+                       _mm_fseek(modreader, sa[t], SEEK_SET);\r
+                       s.length = _mm_read_M_ULONG(modreader);\r
+                       s.type = _mm_read_M_SWORD(modreader);\r
+\r
+                       if (s.type) {\r
+#ifdef MIKMOD_DEBUG\r
+                               fprintf(stderr, "\rNon-sample instruments not supported in MED loader yet\n");\r
+#endif\r
+                               if (!curious) {\r
+                                       _mm_errno = MMERR_MED_SYNTHSAMPLES;\r
+                                       return 0;\r
+                               }\r
+                               s.length = 0;\r
+                       }\r
+\r
+                       if (_mm_eof(modreader)) {\r
+                               _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+                               return 0;\r
+                       }\r
+\r
+                       q->length = s.length;\r
+                       q->seekpos = _mm_ftell(modreader);\r
+                       q->loopstart = ms->sample[t].rep << 1;\r
+                       q->loopend = q->loopstart + (ms->sample[t].replen << 1);\r
+\r
+                       if (ms->sample[t].replen > 1)\r
+                               q->flags |= SF_LOOP;\r
+\r
+                       /* don't load sample if length>='MMD0'...\r
+                          such kluges make libmikmod's code unique !!! */\r
+                       if (q->length >= MMD0_string)\r
+                               q->length = 0;\r
+               } else\r
+                       q->length = 0;\r
+\r
+               if ((mh->MEDEXPP) && (me->exp_smp) &&\r
+                       (t < me->s_ext_entries) && (me->s_ext_entrsz >= 4)) {\r
+                       MEDINSTEXT ie;\r
+\r
+                       _mm_fseek(modreader, me->exp_smp + t * me->s_ext_entrsz,\r
+                                         SEEK_SET);\r
+                       ie.hold = _mm_read_UBYTE(modreader);\r
+                       ie.decay = _mm_read_UBYTE(modreader);\r
+                       ie.suppress_midi_off = _mm_read_UBYTE(modreader);\r
+                       ie.finetune = _mm_read_SBYTE(modreader);\r
+\r
+                       q->speed = finetune[ie.finetune & 0xf];\r
+               } else\r
+                       q->speed = 8363;\r
+\r
+               if ((mh->MEDEXPP) && (me->iinfo) &&\r
+                       (t < me->i_ext_entries) && (me->i_ext_entrsz >= 40)) {\r
+                       MEDINSTINFO ii;\r
+\r
+                       _mm_fseek(modreader, me->iinfo + t * me->i_ext_entrsz, SEEK_SET);\r
+                       _mm_read_UBYTES(ii.name, 40, modreader);\r
+                       q->samplename = DupStr((char*)ii.name, 40, 1);\r
+               } else\r
+                       q->samplename = NULL;\r
+\r
+               q++;\r
+       }\r
+\r
+       if (mh->id == MMD0_string) {\r
+               if (!LoadMEDPatterns()) {\r
+                       _mm_errno = MMERR_LOADING_PATTERN;\r
+                       return 0;\r
+               }\r
+       } else if (mh->id == MMD1_string) {\r
+               if (!LoadMMD1Patterns()) {\r
+                       _mm_errno = MMERR_LOADING_PATTERN;\r
+                       return 0;\r
+               }\r
+       } else {\r
+               _mm_errno = MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       return 1;\r
+}\r
+\r
+CHAR *MED_LoadTitle(void)\r
+{\r
+       ULONG posit, namelen;\r
+       CHAR *name, *retvalue = NULL;\r
+       \r
+       _mm_fseek(modreader, 0x20, SEEK_SET);\r
+       posit = _mm_read_M_ULONG(modreader);\r
+       \r
+       if (posit) {\r
+               _mm_fseek(modreader, posit + 0x2C, SEEK_SET);\r
+               posit = _mm_read_M_ULONG(modreader);\r
+               namelen = _mm_read_M_ULONG(modreader);\r
+\r
+               _mm_fseek(modreader, posit, SEEK_SET);\r
+               name = _mm_malloc(namelen);\r
+               _mm_read_UBYTES(name, namelen, modreader);\r
+               retvalue = DupStr(name, namelen, 1);\r
+               free(name);\r
+       }\r
+\r
+       return retvalue;\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_med = {\r
+       NULL,\r
+       "MED",\r
+       "MED (OctaMED)",\r
+       MED_Init,\r
+       MED_Test,\r
+       MED_Load,\r
+       MED_Cleanup,\r
+       MED_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r