Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_mod.c
diff --git a/apps/plugins/mikmod/loaders/load_mod.c b/apps/plugins/mikmod/loaders/load_mod.c
new file mode 100644 (file)
index 0000000..5166234
--- /dev/null
@@ -0,0 +1,512 @@
+/*     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_mod.c,v 1.2 2004/01/21 13:33:11 raph Exp $\r
+\r
+  Generic MOD loader (Protracker, StarTracker, FastTracker, etc)\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 <ctype.h>\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 MSAMPINFO {\r
+       CHAR samplename[23];            /* 22 in module, 23 in memory */\r
+       UWORD length;\r
+       UBYTE finetune;\r
+       UBYTE volume;\r
+       UWORD reppos;\r
+       UWORD replen;\r
+} MSAMPINFO;\r
+\r
+typedef struct MODULEHEADER {\r
+       CHAR songname[21];                      /* the songname.. 20 in module, 21 in memory */\r
+       MSAMPINFO samples[31];          /* all sampleinfo */\r
+       UBYTE songlength;                       /* number of patterns used */\r
+       UBYTE magic1;                           /* should be 127 */\r
+       UBYTE positions[128];           /* which pattern to play at pos */\r
+       UBYTE magic2[4];                        /* string "M.K." or "FLT4" or "FLT8" */\r
+} MODULEHEADER;\r
+\r
+typedef struct MODTYPE {\r
+       CHAR id[5];\r
+       UBYTE channels;\r
+       CHAR *name;\r
+} MODTYPE;\r
+\r
+typedef struct MODNOTE {\r
+       UBYTE a, b, c, d;\r
+} MODNOTE;\r
+\r
+/*========== Loader variables */\r
+\r
+#define MODULEHEADERSIZE 0x438\r
+\r
+static CHAR protracker[] = "Protracker";\r
+static CHAR startrekker[] = "Startrekker";\r
+static CHAR fasttracker[] = "Fasttracker";\r
+static CHAR oktalyser[] = "Oktalyser";\r
+static CHAR oktalyzer[] = "Oktalyzer";\r
+static CHAR taketracker[] = "TakeTracker";\r
+static CHAR orpheus[] = "Imago Orpheus (MOD format)";\r
+\r
+static MODULEHEADER *mh = NULL;\r
+static MODNOTE *patbuf = NULL;\r
+static int modtype, trekker;\r
+\r
+/*========== Loader code */\r
+\r
+/* given the module ID, determine the number of channels and the tracker\r
+   description ; also alters modtype */\r
+static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
+{\r
+       modtype = trekker = 0;\r
+\r
+       /* Protracker and variants */\r
+       if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {\r
+               *descr = protracker;\r
+               modtype = 0;\r
+               *numchn = 4;\r
+               return 1;\r
+       }\r
+       \r
+       /* Star Tracker */\r
+       if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&\r
+               (isdigit(id[3]))) {\r
+               *descr = startrekker;\r
+               modtype = trekker = 1;\r
+               *numchn = id[3] - '0';\r
+               if (*numchn == 4 || *numchn == 8)\r
+                       return 1;\r
+#ifdef MIKMOD_DEBUG\r
+               else\r
+                       fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);\r
+#endif\r
+               return 0;\r
+       }\r
+\r
+       /* Oktalyzer (Amiga) */\r
+       if (!memcmp(id, "OKTA", 4)) {\r
+               *descr = oktalyzer;\r
+               modtype = 1;\r
+               *numchn = 8;\r
+               return 1;\r
+       }\r
+\r
+       /* Oktalyser (Atari) */\r
+       if (!memcmp(id, "CD81", 4)) {\r
+               *descr = oktalyser;\r
+               modtype = 1;\r
+               *numchn = 8;\r
+               return 1;\r
+       }\r
+\r
+       /* Fasttracker */\r
+       if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {\r
+               *descr = fasttracker;\r
+               modtype = 1;\r
+               *numchn = id[0] - '0';\r
+               return 1;\r
+       }\r
+       /* Fasttracker or Taketracker */\r
+       if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))\r
+               && (isdigit(id[0])) && (isdigit(id[1]))) {\r
+               if (id[3] == 'H') {\r
+                       *descr = fasttracker;\r
+                       modtype = 2;            /* this can also be Imago Orpheus */\r
+               } else {\r
+                       *descr = taketracker;\r
+                       modtype = 1;\r
+               }\r
+               *numchn = (id[0] - '0') * 10 + (id[1] - '0');\r
+               return 1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static BOOL MOD_Test(void)\r
+{\r
+       UBYTE id[4], numchn;\r
+       CHAR *descr;\r
+\r
+       _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);\r
+       if (!_mm_read_UBYTES(id, 4, modreader))\r
+               return 0;\r
+\r
+       if (MOD_CheckType(id, &numchn, &descr))\r
+               return 1;\r
+\r
+       return 0;\r
+}\r
+\r
+static BOOL MOD_Init(void)\r
+{\r
+       if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
+               return 0;\r
+       return 1;\r
+}\r
+\r
+static void MOD_Cleanup(void)\r
+{\r
+       _mm_free(mh);\r
+       _mm_free(patbuf);\r
+}\r
+\r
+/*\r
+Old (amiga) noteinfo:\r
+\r
+_____byte 1_____   byte2_    _____byte 3_____   byte4_\r
+/                \ /      \  /                \ /      \\r
+0000          0000-00000000  0000          0000-00000000\r
+\r
+Upper four    12 bits for    Lower four    Effect command.\r
+bits of sam-  note period.   bits of sam-\r
+ple number.                  ple number.\r
+\r
+*/\r
+\r
+static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)\r
+{\r
+       UBYTE instrument, effect, effdat, note;\r
+       UWORD period;\r
+       UBYTE lastnote = 0;\r
+\r
+       /* extract the various information from the 4 bytes that make up a note */\r
+       instrument = (n->a & 0x10) | (n->c >> 4);\r
+       period = (((UWORD)n->a & 0xf) << 8) + n->b;\r
+       effect = n->c & 0xf;\r
+       effdat = n->d;\r
+\r
+       /* Convert the period to a note number */\r
+       note = 0;\r
+       if (period) {\r
+               for (note = 0; note < 7 * OCTAVE; note++)\r
+                       if (period >= npertab[note])\r
+                               break;\r
+               if (note == 7 * OCTAVE)\r
+                       note = 0;\r
+               else\r
+                       note++;\r
+       }\r
+\r
+       if (instrument) {\r
+               /* if instrument does not exist, note cut */\r
+               if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {\r
+                       UniPTEffect(0xc, 0);\r
+                       if (effect == 0xc)\r
+                               effect = effdat = 0;\r
+               } else {\r
+                       /* Protracker handling */\r
+                       if (!modtype) {\r
+                               /* if we had a note, then change instrument... */\r
+                               if (note)\r
+                                       UniInstrument(instrument - 1);\r
+                               /* ...otherwise, only adjust volume... */\r
+                               else {\r
+                                       /* ...unless an effect was specified, which forces a new\r
+                                          note to be played */\r
+                                       if (effect || effdat) {\r
+                                               UniInstrument(instrument - 1);\r
+                                               note = lastnote;\r
+                                       } else\r
+                                               UniPTEffect(0xc,\r
+                                                                       mh->samples[instrument -\r
+                                                                                               1].volume & 0x7f);\r
+                               }\r
+                       } else {\r
+                               /* Fasttracker handling */\r
+                               UniInstrument(instrument - 1);\r
+                               if (!note)\r
+                                       note = lastnote;\r
+                       }\r
+               }\r
+       }\r
+       if (note) {\r
+               UniNote(note + 2 * OCTAVE - 1);\r
+               lastnote = note;\r
+       }\r
+\r
+       /* Convert pattern jump from Dec to Hex */\r
+       if (effect == 0xd)\r
+               effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);\r
+\r
+       /* Volume slide, up has priority */\r
+       if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))\r
+               effdat &= 0xf0;\r
+\r
+       /* Handle ``heavy'' volumes correctly */\r
+       if ((effect == 0xc) && (effdat > 0x40))\r
+               effdat = 0x40;\r
+       \r
+       /* An isolated 100, 200 or 300 effect should be ignored (no\r
+          "standalone" porta memory in mod files). However, a sequence such\r
+          as 1XX, 100, 100, 100 is fine. */\r
+       if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&\r
+               (lasteffect < 0x10) && (effect != lasteffect))\r
+               effect = 0;\r
+\r
+       UniPTEffect(effect, effdat);\r
+       if (effect == 8)\r
+               of.flags |= UF_PANNING;\r
+       \r
+       return effect;\r
+}\r
+\r
+static UBYTE *ConvertTrack(MODNOTE *n, int numchn)\r
+{\r
+       int t;\r
+       UBYTE lasteffect = 0x10;        /* non existant effect */\r
+\r
+       UniReset();\r
+       for (t = 0; t < 64; t++) {\r
+               lasteffect = ConvertNote(n,lasteffect);\r
+               UniNewline();\r
+               n += numchn;\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+/* Loads all patterns of a modfile and converts them into the 3 byte format. */\r
+static BOOL ML_LoadPatterns(void)\r
+{\r
+       int t, s, tracks = 0;\r
+\r
+       if (!AllocPatterns())\r
+               return 0;\r
+       if (!AllocTracks())\r
+               return 0;\r
+       \r
+       /* Allocate temporary buffer for loading and converting the patterns */\r
+       if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE))))\r
+               return 0;\r
+\r
+       if (trekker && of.numchn == 8) {\r
+               /* Startrekker module dual pattern */\r
+               for (t = 0; t < of.numpat; t++) {\r
+                       for (s = 0; s < (64U * 4); s++) {\r
+                               patbuf[s].a = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].b = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].c = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].d = _mm_read_UBYTE(modreader);\r
+                       }\r
+                       for (s = 0; s < 4; s++)\r
+                               if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
+                                       return 0;\r
+                       for (s = 0; s < (64U * 4); s++) {\r
+                               patbuf[s].a = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].b = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].c = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].d = _mm_read_UBYTE(modreader);\r
+                       }\r
+                       for (s = 0; s < 4; s++)\r
+                               if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
+                                       return 0;\r
+               }\r
+       } else {\r
+               /* Generic module pattern */\r
+               for (t = 0; t < of.numpat; t++) {\r
+                       /* Load the pattern into the temp buffer and convert it */\r
+                       for (s = 0; s < (64U * of.numchn); s++) {\r
+                               patbuf[s].a = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].b = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].c = _mm_read_UBYTE(modreader);\r
+                               patbuf[s].d = _mm_read_UBYTE(modreader);\r
+                       }\r
+                       for (s = 0; s < of.numchn; s++)\r
+                               if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn)))\r
+                                       return 0;\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL MOD_Load(BOOL curious)\r
+{\r
+       int t, scan;\r
+       SAMPLE *q;\r
+       MSAMPINFO *s;\r
+       CHAR *descr;\r
+\r
+       /* try to read module header */\r
+       _mm_read_string((CHAR *)mh->songname, 20, modreader);\r
+       mh->songname[20] = 0;           /* just in case */\r
+\r
+       for (t = 0; t < 31; t++) {\r
+               s = &mh->samples[t];\r
+               _mm_read_string(s->samplename, 22, modreader);\r
+               s->samplename[22] = 0;  /* just in case */\r
+               s->length = _mm_read_M_UWORD(modreader);\r
+               s->finetune = _mm_read_UBYTE(modreader);\r
+               s->volume = _mm_read_UBYTE(modreader);\r
+               s->reppos = _mm_read_M_UWORD(modreader);\r
+               s->replen = _mm_read_M_UWORD(modreader);\r
+       }\r
+\r
+       mh->songlength = _mm_read_UBYTE(modreader);\r
+\r
+       /* this fixes mods which declare more than 128 positions. \r
+        * eg: beatwave.mod */\r
+       if (mh->songlength > 128) { mh->songlength = 128; }\r
+       \r
+       mh->magic1 = _mm_read_UBYTE(modreader);\r
+       _mm_read_UBYTES(mh->positions, 128, modreader);\r
+       _mm_read_UBYTES(mh->magic2, 4, 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 = 6;\r
+       of.inittempo = 125;\r
+       if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {\r
+               _mm_errno = MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       if (trekker && of.numchn == 8)\r
+               for (t = 0; t < 128; t++)\r
+                       /* if module pretends to be FLT8, yet the order table\r
+                          contains odd numbers, chances are it's a lying FLT4... */\r
+                       if (mh->positions[t] & 1) {\r
+                               of.numchn = 4;\r
+                               break;\r
+                       }\r
+       if (trekker && of.numchn == 8)\r
+               for (t = 0; t < 128; t++)\r
+                       mh->positions[t] >>= 1;\r
+\r
+       of.songname = DupStr(mh->songname, 21, 1);\r
+       of.numpos = mh->songlength;\r
+       of.reppos = 0;\r
+\r
+       /* Count the number of patterns */\r
+       of.numpat = 0;\r
+       for (t = 0; t < of.numpos; t++)\r
+               if (mh->positions[t] > of.numpat)\r
+                       of.numpat = mh->positions[t];\r
+\r
+       /* since some old modules embed extra patterns, we have to check the\r
+          whole list to get the samples' file offsets right - however we can find\r
+          garbage here, so check carefully */\r
+       scan = 1;\r
+       for (t = of.numpos; t < 128; t++)\r
+               if (mh->positions[t] >= 0x80)\r
+                       scan = 0;\r
+       if (scan)\r
+               for (t = of.numpos; t < 128; t++) {\r
+                       if (mh->positions[t] > of.numpat)\r
+                               of.numpat = mh->positions[t];\r
+                       if ((curious) && (mh->positions[t]))\r
+                               of.numpos = t + 1;\r
+               }\r
+       of.numpat++;\r
+       of.numtrk = of.numpat * of.numchn;\r
+\r
+       if (!AllocPositions(of.numpos))\r
+               return 0;\r
+       for (t = 0; t < of.numpos; t++)\r
+               of.positions[t] = mh->positions[t];\r
+\r
+       /* Finally, init the sampleinfo structures  */\r
+       of.numins = of.numsmp = 31;\r
+       if (!AllocSamples())\r
+               return 0;\r
+       s = mh->samples;\r
+       q = of.samples;\r
+       for (t = 0; t < of.numins; t++) {\r
+               /* convert the samplename */\r
+               q->samplename = DupStr(s->samplename, 23, 1);\r
+               /* init the sampleinfo variables and convert the size pointers */\r
+               q->speed = finetune[s->finetune & 0xf];\r
+               q->volume = s->volume & 0x7f;\r
+               q->loopstart = (ULONG)s->reppos << 1;\r
+               q->loopend = q->loopstart + ((ULONG)s->replen << 1);\r
+               q->length = (ULONG)s->length << 1;\r
+               q->flags = SF_SIGNED;\r
+               /* Imago Orpheus creates MODs with 16 bit samples, check */\r
+               if ((modtype == 2) && (s->volume & 0x80)) {\r
+                       q->flags |= SF_16BITS;\r
+                       descr = orpheus;\r
+               }\r
+               if (s->replen > 2)\r
+                       q->flags |= SF_LOOP;\r
+\r
+               s++;\r
+               q++;\r
+       }\r
+\r
+       of.modtype = strdup(descr);\r
+\r
+       if (!ML_LoadPatterns())\r
+               return 0;\r
+       \r
+       return 1;\r
+}\r
+\r
+static CHAR *MOD_LoadTitle(void)\r
+{\r
+       CHAR s[21];\r
+\r
+       _mm_fseek(modreader, 0, SEEK_SET);\r
+       if (!_mm_read_UBYTES(s, 20, modreader))\r
+               return NULL;\r
+       s[20] = 0;                                      /* just in case */\r
+\r
+       return (DupStr(s, 21, 1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_mod = {\r
+       NULL,\r
+       "Standard module",\r
+       "MOD (31 instruments)",\r
+       MOD_Init,\r
+       MOD_Test,\r
+       MOD_Load,\r
+       MOD_Cleanup,\r
+       MOD_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r