Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_okt.c
diff --git a/apps/plugins/mikmod/loaders/load_okt.c b/apps/plugins/mikmod/loaders/load_okt.c
new file mode 100644 (file)
index 0000000..e99837d
--- /dev/null
@@ -0,0 +1,461 @@
+/*     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_okt.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  Oktalyzer (OKT) module loader\r
+\r
+==============================================================================*/\r
+\r
+/*\r
+       Written by UFO <ufo303@poczta.onet.pl>\r
+       based on the file description compiled by Harald Zappe\r
+                                                             <zappe@gaea.sietec.de>\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 blocks */\r
+\r
+/* sample information */\r
+typedef struct OKTSAMPLE {\r
+       CHAR sampname[20];\r
+       ULONG len;\r
+       UWORD loopbeg;\r
+       UWORD looplen;\r
+       UBYTE volume;\r
+} OKTSAMPLE;\r
+\r
+typedef struct OKTNOTE {\r
+       UBYTE note, ins, eff, dat;\r
+} OKTNOTE;\r
+\r
+/*========== Loader variables */\r
+\r
+static OKTNOTE *okttrk = NULL;\r
+\r
+/*========== Loader code */\r
+\r
+BOOL OKT_Test(void)\r
+{\r
+       CHAR id[8];\r
+\r
+       if (!_mm_read_UBYTES(id, 8, modreader))\r
+               return 0;\r
+       if (!memcmp(id, "OKTASONG", 8))\r
+               return 1;\r
+\r
+       return 0;\r
+}\r
+\r
+/*     Pattern analysis routine.\r
+       Effects not implemented (yet) : (in decimal)\r
+       11 Arpeggio 4: Change note every 50Hz tick between N,H,N,L\r
+       12 Arpeggio 5: Change note every 50Hz tick between H,H,N\r
+                   N = normal note being played in this channel (1-36)\r
+                   L = normal note number minus upper four bits of 'data'.\r
+                   H = normal note number plus  lower four bits of 'data'.\r
+    13 Decrease note number by 'data' once per tick.\r
+    17 Increase note number by 'data' once per tick.\r
+    21 Decrease note number by 'data' once per line.\r
+    30 Increase note number by 'data' once per line.\r
+*/\r
+static UBYTE *OKT_ConvertTrack(UBYTE patrows)\r
+{\r
+       int t;\r
+       UBYTE ins, note, eff, dat;\r
+\r
+       UniReset();\r
+       for (t = 0; t < patrows; t++) {\r
+               note = okttrk[t].note;\r
+               ins = okttrk[t].ins;\r
+               eff = okttrk[t].eff;\r
+               dat = okttrk[t].dat;\r
+\r
+               if (note) {\r
+                       UniNote(note + 3 * OCTAVE - 1);\r
+                       UniInstrument(ins);\r
+               }\r
+\r
+               if (eff)\r
+                       switch (eff) {\r
+                       case 1:                         /* Porta Up */\r
+                               UniPTEffect(0x1, dat);\r
+                               break;\r
+                       case 2:                         /* Portamento Down */\r
+                               UniPTEffect(0x2, dat);\r
+                               break;\r
+                       /* case 9: what is this? */\r
+                       case 10:                        /* Arpeggio 3 */\r
+                       case 11:                        /* Arpeggio 4 */\r
+                       case 12:                        /* Arpeggio 5 */\r
+                               UniWriteByte(UNI_OKTARP);\r
+                               UniWriteByte(eff + 3 - 10);\r
+                               UniWriteByte(dat);\r
+                               break;\r
+                       case 15:                        /* Amiga filter toggle, ignored */\r
+                               break;\r
+                       case 25:                        /* Pattern Jump */\r
+                               dat = (dat >> 4) * 10 + (dat & 0x0f);\r
+                               UniPTEffect(0xb, dat);\r
+                               break;\r
+                       case 27:                        /* Release - similar to Keyoff */\r
+                               UniWriteByte(UNI_KEYOFF);\r
+                               break;\r
+                       case 28:                        /* Set Tempo */\r
+                               UniPTEffect(0xf, dat & 0x0f);\r
+                               break;\r
+                       case 31:                        /* volume Control */\r
+                               if (dat <= 0x40)\r
+                                       UniPTEffect(0xc, dat);\r
+                               else if (dat <= 0x50)\r
+                                       UniEffect(UNI_XMEFFECTA, (dat - 0x40)); /* fast fade out */\r
+                               else if (dat <= 0x60)\r
+                                       UniEffect(UNI_XMEFFECTA, (dat - 0x50) << 4);    /* fast fade in */\r
+                               else if (dat <= 0x70)\r
+                                       UniEffect(UNI_XMEFFECTEB, (dat - 0x60));        /* slow fade out */\r
+                               else if (dat <= 0x80)\r
+                                       UniEffect(UNI_XMEFFECTEA, (dat - 0x70));        /* slow fade in */\r
+                               break;\r
+#ifdef MIKMOD_DEBUG\r
+                       default:\r
+                               fprintf(stderr, "\rUnimplemented effect (%02d,%02x)\n",\r
+                                               eff, dat);\r
+#endif\r
+                       }\r
+\r
+               UniNewline();\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+/* Read "channel modes" i.e. channel number and panning information */\r
+static void OKT_doCMOD(void)\r
+{\r
+       /* amiga channel panning table */\r
+       UBYTE amigapan[4] = { 0x00, 0xff, 0xff, 0x00 };\r
+       int t;\r
+\r
+       of.numchn = 0;\r
+       of.flags |= UF_PANNING;\r
+\r
+       for (t = 0; t < 4; t++)\r
+               if (_mm_read_M_UWORD(modreader)) {\r
+                       /* two channels tied to the same Amiga hardware voice */\r
+                       of.panning[of.numchn++] = amigapan[t];\r
+                       of.panning[of.numchn++] = amigapan[t];\r
+               } else\r
+                       /* one channel tied to the Amiga hardware voice */\r
+                       of.panning[of.numchn++] = amigapan[t];\r
+}\r
+\r
+/* Read sample information */\r
+static BOOL OKT_doSAMP(int len)\r
+{\r
+       int t;\r
+       SAMPLE *q;\r
+       OKTSAMPLE s;\r
+\r
+       of.numins = of.numsmp = (len / 0x20);\r
+       if (!AllocSamples())\r
+               return 0;\r
+\r
+       for (t = 0, q = of.samples; t < of.numins; t++, q++) {\r
+               _mm_read_UBYTES(s.sampname, 20, modreader);\r
+               s.len = _mm_read_M_ULONG(modreader);\r
+               s.loopbeg = _mm_read_M_UWORD(modreader) * 2;\r
+               s.looplen = _mm_read_M_UWORD(modreader) * 2;\r
+               _mm_read_UBYTE(modreader);\r
+               s.volume = _mm_read_UBYTE(modreader);\r
+               _mm_read_M_UWORD(modreader);\r
+\r
+               if (_mm_eof(modreader)) {\r
+                       _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+                       return 0;\r
+               }\r
+\r
+               if (!s.len)\r
+                       q->seekpos = q->length = q->loopstart = q->loopend = q->flags = 0;\r
+               else {\r
+                       s.len--;\r
+                       /* sanity checks */\r
+                       if (s.loopbeg > s.len)\r
+                               s.loopbeg = s.len;\r
+                       if (s.loopbeg + s.looplen > s.len)\r
+                               s.looplen = s.len - s.loopbeg;\r
+                       if (s.looplen < 2)\r
+                               s.looplen = 0;\r
+\r
+                       q->length = s.len;\r
+                       q->loopstart = s.loopbeg;\r
+                       q->loopend = s.looplen + q->loopstart;\r
+                       q->volume = s.volume;\r
+                       q->flags = SF_SIGNED;\r
+\r
+                       if (s.looplen)\r
+                               q->flags |= SF_LOOP;\r
+               }\r
+               q->samplename = DupStr(s.sampname, 20, 1);\r
+               q->speed = 8287;\r
+       }\r
+       return 1;\r
+}\r
+\r
+/* Read speed information */\r
+static void OKT_doSPEE(void)\r
+{\r
+       int tempo = _mm_read_M_UWORD(modreader);\r
+\r
+       of.initspeed = tempo;\r
+}\r
+\r
+/* Read song length information */\r
+static void OKT_doSLEN(void)\r
+{\r
+       of.numpat = _mm_read_M_UWORD(modreader);\r
+}\r
+\r
+/* Read pattern length information */\r
+static void OKT_doPLEN(void)\r
+{\r
+       of.numpos = _mm_read_M_UWORD(modreader);\r
+}\r
+\r
+/* Read order table */\r
+static BOOL OKT_doPATT(void)\r
+{\r
+       int t;\r
+\r
+       if (!of.numpos || !AllocPositions(of.numpos))\r
+               return 0;\r
+\r
+       for (t = 0; t < 128; t++)\r
+               if (t < of.numpos)\r
+                       of.positions[t] = _mm_read_UBYTE(modreader);\r
+               else\r
+                       break;\r
+\r
+       return 1;\r
+}\r
+\r
+static BOOL OKT_doPBOD(int patnum)\r
+{\r
+       char *patbuf;\r
+       int rows, i;\r
+       int u;\r
+\r
+       if (!patnum) {\r
+               of.numtrk = of.numpat * of.numchn;\r
+\r
+               if (!AllocTracks() || !AllocPatterns())\r
+                       return 0;\r
+       }\r
+\r
+       /* Read pattern */\r
+       of.pattrows[patnum] = rows = _mm_read_M_UWORD(modreader);\r
+\r
+       if (!(okttrk = (OKTNOTE *) _mm_calloc(rows, sizeof(OKTNOTE))) ||\r
+           !(patbuf = (char *)_mm_calloc(rows * of.numchn, sizeof(OKTNOTE))))\r
+               return 0;\r
+       _mm_read_UBYTES(patbuf, rows * of.numchn * sizeof(OKTNOTE), modreader);\r
+       if (_mm_eof(modreader)) {\r
+               _mm_errno = MMERR_LOADING_PATTERN;\r
+               return 0;\r
+       }\r
+\r
+       for (i = 0; i < of.numchn; i++) {\r
+               for (u = 0; u < rows; u++) {\r
+                       okttrk[u].note = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE)];\r
+                       okttrk[u].ins = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 1];\r
+                       okttrk[u].eff = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 2];\r
+                       okttrk[u].dat = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 3];\r
+               }\r
+\r
+               if (!(of.tracks[patnum * of.numchn + i] = OKT_ConvertTrack(rows)))\r
+                       return 0;\r
+       }\r
+       _mm_free(patbuf);\r
+       _mm_free(okttrk);\r
+       return 1;\r
+}\r
+\r
+static void OKT_doSBOD(int insnum)\r
+{\r
+       of.samples[insnum].seekpos = _mm_ftell(modreader);\r
+}\r
+\r
+BOOL OKT_Load(BOOL curious)\r
+{\r
+       UBYTE id[4];\r
+       ULONG len;\r
+       ULONG fp;\r
+       BOOL seen_cmod = 0, seen_samp = 0, seen_slen = 0, seen_plen = 0, seen_patt\r
+                       = 0, seen_spee = 0;\r
+       int patnum = 0, insnum = 0;\r
+       (void)curious;\r
+\r
+       /* skip OKTALYZER header */\r
+       _mm_fseek(modreader, 8, SEEK_SET);\r
+       of.songname = strdup("");\r
+\r
+       of.modtype = strdup("Amiga Oktalyzer");\r
+       of.numpos = of.reppos = 0;\r
+       \r
+       /* default values */\r
+       of.initspeed = 6;\r
+       of.inittempo = 125;\r
+\r
+       while (1) {\r
+               /* read block header */\r
+               _mm_read_UBYTES(id, 4, modreader);\r
+               len = _mm_read_M_ULONG(modreader);\r
+               \r
+               if (_mm_eof(modreader))\r
+                       break;\r
+               fp = _mm_ftell(modreader);\r
+               \r
+               if (!memcmp(id, "CMOD", 4)) {\r
+                       /*if (!seen_cmod) {\r
+                               OKT_doCMOD();  // gcc for ipod stucks here, saying it is a call to memcpy...\r
+                               seen_cmod = 1;\r
+                       } else {*/\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       /* } */\r
+               } else if (!memcmp(id, "SAMP", 4)) {\r
+                       if (!seen_samp && OKT_doSAMP(len))\r
+                               seen_samp = 1;\r
+                       else {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id, "SPEE", 4)) {\r
+                       if (!seen_spee) {\r
+                               OKT_doSPEE();\r
+                               seen_spee = 1;\r
+                       } else {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id, "SLEN", 4)) {\r
+                       if (!seen_slen) {\r
+                               OKT_doSLEN();\r
+                               seen_slen = 1;\r
+                       } else {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id, "PLEN", 4)) {\r
+                       if (!seen_plen) {\r
+                               OKT_doPLEN();\r
+                               seen_plen = 1;\r
+                       } else {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id, "PATT", 4)) {\r
+                       if (!seen_plen) {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+                       if (!seen_patt && OKT_doPATT())\r
+                               seen_patt = 1;\r
+                       else {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id,"PBOD", 4)) {\r
+                       /* need to know numpat and numchn */\r
+                       if (!seen_slen || !seen_cmod || (patnum >= of.numpat)) {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+                       if (!OKT_doPBOD(patnum++)) {\r
+                               _mm_errno = MMERR_LOADING_PATTERN;\r
+                               return 0;\r
+                       }\r
+               } else if (!memcmp(id,"SBOD",4)) {\r
+                       /* need to know numsmp */\r
+                       if (!seen_samp) {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+                       while ((insnum < of.numins) && !of.samples[insnum].length)\r
+                               insnum++;\r
+                       if (insnum >= of.numins) {\r
+                               _mm_errno = MMERR_LOADING_HEADER;\r
+                               return 0;\r
+                       }\r
+                       OKT_doSBOD(insnum++);\r
+               }\r
+\r
+               /* goto next block start position */\r
+               _mm_fseek(modreader, fp + len, SEEK_SET);\r
+       }\r
+\r
+       if (!seen_cmod || !seen_samp || !seen_patt ||\r
+               !seen_slen || !seen_plen || (patnum != of.numpat)) {\r
+               _mm_errno = MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
+CHAR *OKT_LoadTitle(void)\r
+{\r
+       return strdup("");\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_okt = {\r
+       NULL,\r
+       "OKT",\r
+       "OKT (Amiga Oktalyzer)",\r
+       NULL,\r
+       OKT_Test,\r
+       OKT_Load,\r
+       NULL,\r
+       OKT_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r