Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_asy.c
diff --git a/apps/plugins/mikmod/loaders/load_asy.c b/apps/plugins/mikmod/loaders/load_asy.c
new file mode 100644 (file)
index 0000000..6b78e31
--- /dev/null
@@ -0,0 +1,399 @@
+/*     MikMod sound library\r
+       (c) 2004, Raphael Assenat and others - see file AUTHORS for\r
+       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_asy.c,v 1.3 2004/01/28 01:18:22 raph Exp $\r
+\r
+  ASYLUM Music Format v1.0 (.amf) loader\r
+  adapted from load_mod.c by Raphael Assenat <raph@raphnet.net>,\r
+  with the help of the AMF2MOD utility sourcecode,\r
+  written to convert crusader's amf files into 8\r
+  channels mod file in 1995 by Mr. P / Powersource\r
+  mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca \r
+  \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 <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+/*========== Module structure */\r
+\r
+typedef struct MSAMPINFO {\r
+       CHAR samplename[24];\r
+       UBYTE finetune;\r
+       UBYTE volume;\r
+       ULONG length;\r
+       ULONG reppos;\r
+       ULONG replen;\r
+} MSAMPINFO;\r
+\r
+typedef struct MODULEHEADER {\r
+       CHAR songname[21];\r
+       UBYTE num_patterns;     /* number of patterns used */\r
+       UBYTE num_orders;\r
+       UBYTE positions[256];   /* which pattern to play at pos */\r
+       MSAMPINFO samples[64];  /* all sampleinfo */\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
+/* This table is taken from AMF2MOD.C\r
+ * written in 1995 by Mr. P / Powersource\r
+ * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */ \r
+UWORD periodtable[]={6848,6464,6096,5760,5424,5120,4832,4560,4304,\r
+       4064,3840,3628,3424,3232,3048,2880,2712,2560,\r
+       2416,2280,2152,2032,1920,1814,1712,1616,1524,\r
+       1440,1356,1280,1208,1140,1076,1016, 960, 907,\r
+       856, 808, 762, 720, 678, 640, 604, 570, 538,\r
+       508, 480, 453, 428, 404, 381, 360, 339, 320,\r
+       302, 285, 269, 254, 240, 226, 214, 202, 190,\r
+       180, 170, 160, 151, 143, 135, 127, 120, 113,\r
+       107, 101,  95,  90,  85,  80,  75,  71,  67,\r
+       63,  60,  56,  53,  50,  47,  45,  42,  40,\r
+       37,  35,  33,  31,  30,  28};\r
+\r
+/*========== Loader variables */\r
+\r
+static CHAR asylum[] = "Asylum 1.0";\r
+\r
+static MODULEHEADER *mh = NULL;\r
+static MODNOTE *patbuf = NULL;\r
+static int modtype = 0;\r
+\r
+/*========== Loader code */\r
+\r
+static BOOL ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
+{\r
+       if (!memcmp(id, "ASYLUM Music Format V1.0", 24))\r
+       {\r
+               *descr = asylum;\r
+               *numchn = 8;\r
+               modtype = 1;\r
+               return 1;\r
+       }\r
+       \r
+       return 0;\r
+}\r
+\r
+static BOOL ASY_Test(void)\r
+{\r
+       UBYTE namestring[24], numchn;\r
+       CHAR *descr;\r
+\r
+       /* Read the magic string */\r
+       _mm_fseek(modreader, 0, SEEK_SET);\r
+       if (!_mm_read_UBYTES(namestring, 24, modreader))\r
+               return 0;\r
+\r
+       /* Test if the string is what we expect */\r
+       if (ASY_CheckType(namestring, &numchn, &descr))\r
+               return 1;\r
+\r
+       return 0;\r
+}\r
+\r
+static BOOL ASY_Init(void)\r
+{\r
+       if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
+               return 0;\r
+       return 1;\r
+}\r
+\r
+static void ASY_Cleanup(void)\r
+{\r
+       _mm_free(mh);\r
+       _mm_free(patbuf);\r
+}\r
+\r
+static void ConvertNote(MODNOTE *n)\r
+{\r
+       UBYTE instrument, effect, effdat, note;\r
+       UWORD period;\r
+       UBYTE lastnote = 0;\r
+       \r
+       instrument = n->b&0x1f;\r
+       effect = n->c;\r
+       effdat = n->d;\r
+\r
+       /* convert amf note to mod period */\r
+       if (n->a) {\r
+               period = periodtable[n->a];\r
+       } else {\r
+               period = 0;\r
+       }\r
+       \r
+       /* Convert the period to a note number */\r
+       note = 0;\r
+       if (period) \r
+       {\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, \r
+                                        * which forces a new note to be \r
+                                        * 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
+       UniPTEffect(effect, effdat);\r
+}\r
+\r
+static UBYTE *ConvertTrack(MODNOTE *n)\r
+{\r
+       int t;\r
+\r
+       UniReset();\r
+       for (t = 0; t < 64; t++) {\r
+               ConvertNote(n);\r
+               UniNewline();\r
+               n += of.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
+       }\r
+       if (!AllocTracks()) {\r
+               return 0;\r
+       }\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
+\r
+       /* patterns start here */\r
+       _mm_fseek(modreader, 0xA66, SEEK_SET);\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))) {\r
+                               return 0;\r
+                       }\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL ASY_Load(BOOL curious)\r
+{\r
+       int t;\r
+       SAMPLE *q;\r
+       MSAMPINFO *s;\r
+       CHAR *descr=asylum;\r
+       ULONG seekpos;\r
+       (void)curious;\r
+\r
+       // no title in asylum amf files :(\r
+       strcpy(mh->songname, "");\r
+\r
+       _mm_fseek(modreader, 0x23, SEEK_SET);\r
+       mh->num_patterns = _mm_read_UBYTE(modreader);\r
+       mh->num_orders = _mm_read_UBYTE(modreader);\r
+       \r
+       // skip unknown byte\r
+       _mm_read_UBYTE(modreader);\r
+       _mm_read_UBYTES(mh->positions, 256, modreader);\r
+       \r
+       /* read samples headers*/\r
+       for (t = 0; t < 64; t++) {\r
+               s = &mh->samples[t];\r
+               \r
+               _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);\r
+               \r
+               _mm_read_string(s->samplename, 22, modreader);\r
+               s->samplename[21] = 0;  /* just in case */\r
+               \r
+               s->finetune = _mm_read_UBYTE(modreader);\r
+               s->volume = _mm_read_UBYTE(modreader);\r
+               _mm_read_UBYTE(modreader); // skip unknown byte\r
+               s->length = _mm_read_I_ULONG(modreader);\r
+               s->reppos = _mm_read_I_ULONG(modreader);\r
+               s->replen = _mm_read_I_ULONG(modreader);\r
+       }\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
+       of.numchn = 8;\r
+       modtype = 0;\r
+       of.songname = DupStr(mh->songname, 21, 1);\r
+       of.numpos = mh->num_orders;\r
+       of.reppos = 0;\r
+       of.numpat = mh->num_patterns;\r
+       of.numtrk = of.numpat * of.numchn;\r
+\r
+       \r
+       /* Copy positions (orders) */\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
+\r
+       /* Finally, init the sampleinfo structures  */\r
+       of.numins = 31;\r
+       of.numsmp = 31;\r
+       if (!AllocSamples())\r
+               return 0;\r
+       s = mh->samples;\r
+       q = of.samples;\r
+       seekpos = 2662+(2048*(of.numpat));\r
+       for (t = 0; t < of.numins; t++) {\r
+               /* convert the samplename */\r
+               q->samplename = DupStr(s->samplename, 23, 1);\r
+               \r
+               /* init the sampleinfo variables */\r
+               q->speed = finetune[s->finetune & 0xf];\r
+               q->volume = s->volume & 0x7f;\r
+\r
+               q->loopstart = (ULONG)s->reppos;\r
+               q->loopend = (ULONG)q->loopstart + (s->replen);\r
+               q->length = (ULONG)s->length;\r
+               \r
+               q->flags = SF_SIGNED;\r
+       \r
+               q->seekpos = seekpos;\r
+               seekpos += q->length;\r
+               \r
+               if ((s->replen) > 2) {\r
+                       q->flags |= SF_LOOP;\r
+               }\r
+               \r
+               /* fix replen if repend > length */\r
+               if (q->loopend > q->length)\r
+                       q->loopend = q->length;\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 *ASY_LoadTitle(void)\r
+{\r
+       CHAR *s = ""; // no titles\r
+\r
+       return (DupStr(s, 21, 1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MLOADER load_asy = {\r
+       NULL,\r
+       "AMF",\r
+       "AMF (ASYLUM Music Format V1.0)",\r
+       ASY_Init,\r
+       ASY_Test,\r
+       ASY_Load,\r
+       ASY_Cleanup,\r
+       ASY_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r